Appearance
2026-07-14 — Busted Player Disposition shipped
A cash player who busts (0 chips at hand end) now gets a clean, uniform disposition — a rebuy window, then rebuy-and-play-on or seat-freed — instead of silently wedging (or worse) the table. Fixes the 8I21MZ class found during the agent-decision-inspection smoke, ahead of agent-table go-live.
See the current-state write-up in table-lifecycle.
The bug this closes
The poker engine NULLs a busted seat at showdown. Every downstream consumer tripped over that null:
- The engine→PM stack sync skipped null seats, so the boundary checkpoint persisted the busted seat's stale pre-hand stack (observed: a seat that busted to 0 read
13). - That stale balance made
table_playerssum diverge from the on-chain pool (e.g.293vs200) — a conservation divergence that tripped the custody invariant into a freeze → auto-escape of the whole table on any heads-up bust (the winner lost their seat too). - The stale balance also defeated the bank's
chipAmount <= 0forced-withdraw guard — a system-initiated leave would have paid out chips the player no longer held.
What shipped
- True-0 at the boundary. Busts are detected from the committed hand-end payload (
endStack === 0), never the nulled engine seats. The busted seat's PM balance is zeroed before the checkpoint reads it, sotable_playersstays settlement-safe and the spurious freeze→escape is gone (delta= 0). - Rebuy window.
seated → sitting_outat the boundary, then a per-tablebustRebuyWindowSeconds(zod 5–600, default 60) window; abust_rebuy_windowbroadcast drives a live countdown on the seat owner's<RebuyAffordance>. - Two outcomes. A credited deposit inside the window re-seats the player (
sitting_out → seated, via an explicit credit-chips branch — a busted player can't ride the self-heal reserve path sincesitting_out → reservedis an invalid transition). No rebuy → a local 0-chip stand-up (no bank leg — the bank 400snothing_to_withdrawby design), the row reachesgone, the seat frees. - Restart-safe. Reconstruction re-arms each open window from
last_checkpoint_at + bustRebuyWindowSeconds; a past deadline disposes immediately. A reconstruction-parity fix:pm.standUpnow tolerates an unallocated 0-chip ledger seat (a reconstructed busted seat isn't ledger-allocated). - Agents ride the identical path (no
isAgentbranch). - Telemetry:
poker.bust_dispositionspan per bust withdisposition.outcome ∈ {sat_out, rebuy, stood_up, rearmed_at_boot}.
Verified
13 integration tests on the test profile (true-0, sitting-out, rebuy window, rebuy re-seats, expiry frees the seat, no money on expiry, heads-up never wedges, winner unaffected, restart mid-window resolves, forced-withdraw pays 0)
- RTL countdown coverage. The live agent-bust seat cleanup (T8) is deferred to the hosted-agent dogfood (the disposition is agent-agnostic). The stranded dev artifacts
8I21MZ/334015/334017are concluded.
The full decision record is at .indusk/planning/busted-player-disposition/adr.md (published to the decisions section at plan close).