Backports the legacy brain.js escrow interlock (from the public-domain
lamassu-machine tree at c0b69d1, see CLAUDE.md provenance):
- The id003/ebds drivers' `billsValid` event (bill physically reached
the stacker) is now the credit trigger. hal-service tracks
escrow → in-flight and fires onBillInserted only on confirmation;
hal:stack-bill no longer synthesizes the credit at command time.
- New BILL_PENDING machine event marks the in-flight bill;
FINISH_INSERTING is guard-blocked while one is pending, so "done"
pressed mid-stack can no longer mint an LNURL that includes a bill
still sitting in escrow (the aiolabs/bitspire#58 loss).
- BILL_INSERTED now requires a matching pending bill (stray or
out-of-state confirmations are never credited) and BILL_REJECTED
clears the in-flight marker — a failed/returned stack was never
credited, so nothing to unwind.
- Escrow decision is fail-closed (legacy _billsRead parity): bill read
outside insertingBills, or with unknown rate/balance, is returned to
the customer instead of stacked-and-swallowed (closes the #35 gap at
the decision point that physically takes the money).
- CashInView disables "Done" and shows a processing hint while a bill
is in flight; the dev simulator drives the same guarded two-event
path.
Both loss directions verified against the legacy semantics:
operator-pays-for-unstacked-cash and customer-bill-swallowed-uncredited.
6 new state-machine interlock tests; 27 state-machine + 43 machine-app
tests pass; full build (vue-tsc + vite + electron tsc) clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>