diff --git a/apps/machine/src/services/operator-config.ts b/apps/machine/src/services/operator-config.ts index e9f38b9..549719f 100644 --- a/apps/machine/src/services/operator-config.ts +++ b/apps/machine/src/services/operator-config.ts @@ -65,7 +65,21 @@ type CassetteOp = { * machine" gesture and already clears counts-uncertain. */ type ResumeCashOutOp = { id: string; at: number; type: 'resume_cash_out' } -type OperatorOp = CassetteOp | ResumeCashOutOp +/** + * ADR-005 §6: the operator paid the customer by hand, off-machine, for a + * cash-out this machine recorded as dispense_error / partial. Flips that row + * to `remediated` with the note as provenance so both ledgers close on one + * act. Idempotent by nature — remediateTransaction only touches rows still + * in an error state — so re-delivery is harmless. + */ +type SettleTransactionOp = { + id: string + at: number + type: 'settle_transaction' + txid: string + note?: string +} +type OperatorOp = CassetteOp | ResumeCashOutOp | SettleTransactionOp /** Accept operator events stamped up to this many seconds in the future. */ const MAX_FUTURE_SKEW_S = 60 @@ -249,7 +263,22 @@ async function handleOperatorConfigEvent( const resumeOps = allOps.filter( (o): o is ResumeCashOutOp => !!o && o.type === 'resume_cash_out' ) - const ops = allOps.filter((o): o is CassetteOp => !!o && o.type !== 'resume_cash_out') + const settleOps = allOps.filter( + (o): o is SettleTransactionOp => + !!o && o.type === 'settle_transaction' && typeof (o as SettleTransactionOp).txid === 'string' + ) + for (const op of settleOps) { + const provenance = `settled-off-machine:${op.id}${op.note ? `:${op.note}` : ''}` + const changed = await api.remediateTransaction(op.txid, provenance) + console.log( + `[OperatorConfig] settle_transaction ${op.id} for ${op.txid}: ` + + (changed ? 'row marked remediated' : 'no row in an error state (already closed, or unknown)') + ) + } + const ops = allOps.filter( + (o): o is CassetteOp => + !!o && o.type !== 'resume_cash_out' && o.type !== 'settle_transaction' + ) if (holdBefore && resumeOps.some((o) => typeof o.at === 'number' && o.at > holdBefore.since)) { await api.clearCashOutHold() console.log(