Skip to content

2026-05-19 — SnG Tournaments closed

Accepted 2026-05-13, amended 2026-05-15 (orchestrator pattern), all six phases closed by 2026-05-19.

What shipped

  • One game type, two modes. tables.mode = 'cash' | 'sng'. Same gameType='poker', same PokerTable. Tournament-specific behavior owned by a TournamentOrchestrator instantiated only when mode='sng'.
  • Lifecycle. registering → starting → playing → ended → settling → closed. Server-assigned seats (Fisher-Yates) at the registering → starting transition. Engine seating with starting-stack tournament chips at starting → playing.
  • Blind escalation timer armed at playing, wall-clock based, restart-safe via persisted level_started_at. Engine's forcedBets synced before each deal — no race between "blinds changed" and "next deal."
  • Bust detection + elimination ordering inside the persistence-boundary transaction. Multi-way busts on one hand ordered by preHandStack desc. Post-commit broadcasts: player_eliminated → fresh game_state (refreshes ranks) → hand_result.
  • Multi-position settlement via payout_intents (one row per position, UNIQUE (table_id, position)). Admin-server /api/withdraw branches on body.payoutIntent; reconciler sweeps stuck rows every 30s.
  • Agent surface extends game_state with three optional fields (tournament, tournamentSchedule, tournamentMyView). New poker:sng skill in hosted-agent-service. agent-connection.ts routes per-turn on state.tournament !== undefined.
  • tournament.lifecycle root span — one per tournament lifetime. Seven config attrs at open, state_transition event per transition, three final attrs at close. auto_fold_count surfaces aggressive actionTimeoutSeconds as queryable.
  • Force-end path for stuck tournaments. Admin UI two-step button on /tournaments/[tableId] when state='playing' AND levelAge > 5min. Routes through admin-server (service-key) → game-server /internal/force-end-tournamentorchestrator.forceEnd().
  • Restart safety via tables.tournament_state, current_level, level_started_at. resumeFromBoot() re-arms the blind timer with missed-escalation clamping.

What this closes

The cash code path stayed simple — PokerTable carries four small mode-aware inflection points (blind source, bust reporting, stand_up rejection, forced auto-blinds). Everything else lives in the orchestrator. The tournament infrastructure reused every line of the engine, persistence layer, telemetry contracts, and UI surface.

Phase 6's user-facing surface (BlindLadder UI, per-seat ICM equity arcs, per-seat hand equity, agent thoughts log) landed alongside spectator-UI and anomaly fixes on the same day — the informal SnG dogfood was satisfied by a 90-hand LLM-driven NPC smoke with zero anomalies.

Carry forwards

  • Bug 15 — a separate follow-up, surfaced by Phase 10 cash dogfood, applies to BOTH cash and tournament concurrent-seat flows
  • Agent table-talk and analysis — today's spectator log is v0.5; the full surface is still queued
  • LLM reason field in agent_thought broadcasts — captured as a small follow-up

Now-current pages

See also