ADR-005 slice 2: operator alert, settle off-machine, glossary + guide #50

Merged
padreug merged 6 commits from feat/dispense-outcome-slice2 into main 2026-10-10 20:30:36 +00:00
Owner

Second half of bitspire ADR-005 (#49 was the first). Pairs with bitspire feat/settle-transaction-op, which teaches the machine the new op — merge order doesn't matter, an unknown op type is skipped and stays pending until the machine learns it.

Operator alert (m017, notify.py). When a settlement lands in cash_owed or partial_pending, the operator gets a NIP-17 gift-wrapped DM (kind 14 → 13 → 1059) to their own account pubkey, or to the new super_config.alerts_pubkey. Signed through the operator's signer, so nothing new sits at rest. Best-effort: a failed publish is logged and the dispense report is still acked; operator_notified_at stops a resend from re-alerting.

Settle off-machine. POST /api/v1/dca/settlements/{id}/settle-cash-owed with a provenance note: the settlement distributes at the full amount (the customer is whole) and a machine-wide settle_transaction {txid, note} op is published so the machine marks its own row remediated. Until now the only way to close an owed row was to dispense more cash from the machine that had just jammed. Action is on the worklist buckets and the settlements table.

Docs. static/docs/errors.html (glossary keyed by raw code — 78 42, 82 00, … — with class, meaning, what you'll find inside) and static/docs/operator-guide.html, both linked from the dashboard header; the worklist's error cell deep-links to the glossary entry.

Not in this PR: the cash_out_enabled switch — it carries its own machine-side state and is additive to Resume; filed separately.

Tests: 294 pass (28 new). ruff/mypy findings are the pre-existing set on main.

Fits #40.

Second half of bitspire ADR-005 (#49 was the first). Pairs with bitspire `feat/settle-transaction-op`, which teaches the machine the new op — merge order doesn't matter, an unknown op type is skipped and stays pending until the machine learns it. **Operator alert (m017, `notify.py`).** When a settlement lands in `cash_owed` or `partial_pending`, the operator gets a NIP-17 gift-wrapped DM (kind 14 → 13 → 1059) to their own account pubkey, or to the new `super_config.alerts_pubkey`. Signed through the operator's signer, so nothing new sits at rest. Best-effort: a failed publish is logged and the dispense report is still acked; `operator_notified_at` stops a resend from re-alerting. **Settle off-machine.** `POST /api/v1/dca/settlements/{id}/settle-cash-owed` with a provenance note: the settlement distributes at the full amount (the customer is whole) and a machine-wide `settle_transaction {txid, note}` op is published so the machine marks its own row remediated. Until now the only way to close an owed row was to dispense more cash from the machine that had just jammed. Action is on the worklist buckets and the settlements table. **Docs.** `static/docs/errors.html` (glossary keyed by raw code — `78 42`, `82 00`, … — with class, meaning, what you'll find inside) and `static/docs/operator-guide.html`, both linked from the dashboard header; the worklist's error cell deep-links to the glossary entry. Not in this PR: the `cash_out_enabled` switch — it carries its own machine-side state and is additive to Resume; filed separately. Tests: 294 pass (28 new). ruff/mypy findings are the pre-existing set on main. Fits #40.
ADR-005 §6 groundwork. settle_transaction is a machine-wide op like
resume_cash_out; it carries the machine txid it closes and the operator's
provenance note. super_config.alerts_pubkey overrides where owed-cash
alerts go; dca_settlements.operator_notified_at stops a report resend
from re-alerting.
cash_owed / partial_pending now sends a NIP-17 gift-wrapped DM (kind 14
rumor → kind 13 seal signed through the operator's signer → kind 1059
wrap from a throwaway key) to the operator's own account pubkey, or to
super_config.alerts_pubkey when set. Best-effort: a failed publish is
logged and the dispense report is still acked; the worklist is the
durable record. No email channel, no NIP-04.
POST /api/v1/dca/settlements/{id}/settle-cash-owed: the operator paid
the customer by hand. Appends the provenance note, distributes at the
full amount (the customer is whole), and publishes a settle_transaction
op so the machine flips its own dispense_error/partial row to
remediated. Until now the only way to close an owed row was to dispense
more cash from the machine that had just jammed.
Settle action on the cash_owed/partial_pending worklist buckets and the
settlements table; the worklist's error cell links the raw code to the
glossary entry; the platform settings dialog gains the alerts pubkey.
Served from the extension's static dir and linked from the dashboard
header. The glossary is keyed by raw code (78 42, 82 00, …) with the
class, what it means, and what to do inside the unit; the guide covers
bays/ops ownership, the authorize/capture lifecycle, the cash-out hold,
owed-cash resolution, and alerts.
test: slice-2 coverage — NIP-17 round trip, alert policy, settle path, op shape
Some checks failed
ci.yml / test: slice-2 coverage — NIP-17 round trip, alert policy, settle path, op shape (pull_request) Failing after 0s
1e235e3e42
padreug deleted branch feat/dispense-outcome-slice2 2026-10-10 20:30:36 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/spirekeeper!50
No description provided.