Appearance
2026-06-12 — Player-funds lifecycle audit shipped
The full audit closed across six phases. Table states mean something now, the player journey survives a restart at every stage, and the trace is the history.
What changed
- One table-state machine.
transitionTableState()is the only writer oftables.status/freeze_reason/closed_at— legal transitions enforced (open⇄frozen, open→closed, frozen→closed), atomic writes, atable.state_changedspan per transition, livetable_state_changedbroadcast to connected clients, and the lobby drops frozen/closed tables. Custody freeze, close-on-empty, unfreeze, and escape all migrated onto it. - Participant end-of-life. Cash-out zeroes the row, frees the seat, and unbinds the decision agent in one atomic write (
state='gone'). Forced withdraws re-read the row and 409already_settled;sit_ingates on the authoritative DB balance (sit_in_no_chips). - No seated-with-0-chips window. The reserved→seated broadcast is deferred until the engine seat carries the chips.
- Restarts heal. Reconstruction mirrors the engine (stack = chip_balance), pending blind requests are replayed to reconnecting clients with their original deadlines, sit-out state writes through to the DB, and mid-hand restarts void to the checkpoint without tripping the conservation alarm. Verified by a stage-parameterized restart matrix (
test/lifecycle/restart-matrix.test.ts). - Escape pays for real. Two bugs fixed: the escape route was Sepolia-only (couldn't pay on local), and it read the wrong snapshot field (
chipBalancevschipCount) — every escape paid 0 while looking successful. Escapes now pay the exact per-player split on-chain and close the table. - Abandoned tables reach a conclusion. The idle reaper closes empty idle tables and escapes funded abandoned ones (thresholds in
game_settings). - Telemetry completeness.
participant.seated/participant.stood_upspans, atable.rejectionspan for every coded WS rejection (emitted at thesendTochokepoint),table.reconstructionat boot, and the full journey is replayable from spans alone (T22).
Hardening pass (post-close)
A post-close hardening pass added seven confirmed-then-fixed fixes: reaper eligibility blind spots (in-flight deposits, registrant equity, non-poker liveness), escape underpaying sitting-out players, the seated broadcast firing on the engine-failure path, the state machine's same-state heal, and on-chain closeTable convergence (a new admin-server sweep — which found a real stranded table on its first boot). Escape memorialization (T33) was handed to the custody-layer plan.
Now-current pages
- Table Lifecycle (new)
- Table frozen — recovery runbook (rewritten around the working unfreeze/escape)