refactor(v2): cassette CRUD + transport — position-keyed (#29 v1.1)

CRUD layer flips:
  - get_cassette_config(machine_id, position) — was (..., denomination)
  - list_cassette_configs_for_machine returns ORDER BY position alone
    (no secondary denomination ordering — position is the unique key)
  - update_cassette_config(machine_id, position, data, updated_by) —
    operator edits denomination + count for a fixed slot
  - apply_bootstrap_state upserts ON CONFLICT(machine_id, position)
    iterating payload.positions; populates new state_denomination
    column from row.denomination alongside state_count

cassette_transport.py needs almost no functional change — the wire
shape is implicit via PublishCassettesPayload.to_wire_dict (now emits
{"positions": {...}}) and decrypt_and_parse_state_event accepts what
the model parses. Just the module docstring + the publish log line
get updated to reference positions rather than denominations.

Tests still red until commit f rewrites them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-05-30 22:25:27 +02:00
commit 5dbd7314f4
2 changed files with 38 additions and 34 deletions

View file

@ -3,7 +3,7 @@ Cassette-config Nostr transport — operator ↔ ATM kind-30078 publish + consum
Per the locked design at aiolabs/satmachineadmin#29 (paired with
lamassu-next#56) and the dcd0874 privacy-by-default pivot, the operator
publishes denomination-keyed cassette config to a target ATM via:
publishes position-keyed cassette config to a target ATM via:
kind = 30078 (NIP-78, replaceable)
tags = [
@ -304,7 +304,7 @@ async def publish_to_atm(
logger.info(
f"satmachineadmin: published kind-30078 cassette config to ATM "
f"{atm_pubkey_hex[:12]}... (event_id={signed['id'][:12]}..., "
f"machine_id={machine.id}, denominations={list(payload.denominations.keys())})"
f"machine_id={machine.id}, positions={sorted(payload.positions.keys())})"
)
return signed