felt/.planning/phases/01-tournament-engine/01-CONTEXT.md
Mikkel Georgsen 21ff95068e docs(01): create Phase 1 plans (A-N) with research and feedback
14 plans in 6 waves covering all 68 requirements for the Tournament
Engine phase. Includes research (go-libsql, NATS JetStream, Svelte 5
runes, ICM complexity), plan verification (2 iterations), and user
feedback (hand-for-hand UX, SEAT-06 reword, re-entry semantics,
integration test, DKK defaults, JWT 7-day expiry, clock tap safety).

Wave structure:
  1: A (scaffold), B (schema)
  2: C (auth/audit), D (clock), E (templates), J (frontend scaffold)
  3: F (financial), H (seating), M (layout shell)
  4: G (player management)
  5: I (tournament lifecycle)
  6: K (overview/financials), L (players), N (tables/more)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 02:58:22 +01:00

147 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 1: Tournament Engine - Context
**Gathered:** 2026-03-01
**Status:** Ready for planning
<domain>
## Phase Boundary
A complete tournament runs from start to finish in an x86 LXC container with a working touch-friendly operator (TD) UI — demoable at a venue by pointing any device at the container. Go backend with embedded LibSQL, NATS JetStream, WebSocket hub. SvelteKit frontend embedded via go:embed. Covers: clock, blinds, financials, players, tables, seating, multi-tournament, and the full TD interface.
Note: The operator throughout this system is the **Tournament Director (TD)** — use this term consistently.
</domain>
<decisions>
## Implementation Decisions
### Tournament Setup Flow
- **Template-first creation** — TD picks from saved tournament templates, everything pre-fills, tweak what's needed for tonight, hit Start
- **Templates are compositions of reusable building blocks** — a template is NOT a monolithic config. It references:
- Chip set (denominations, colors, values — venue-level)
- Blind structure (level progression, antes, breaks)
- Payout structure (tiered by entry count — see Payout Structure below)
- Buy-in config (entry cost, rake splits, rebuy/add-on rules)
- Points formula (league scoring — venue-level, reused across seasons)
- **Local changes by default** — when creating a tournament from a template, the TD gets a copy. Edits only affect that tournament. To change the actual reusable block, go to template management
- **Dedicated template management area** — create from scratch, duplicate/edit existing, save tournament config as new template
- **Built-in starter templates** ship with the app (Turbo, Standard, Deep Stack, WSOP-style) so day one has something to pick from
- **Structure wizard** lives in template management — input player count, starting chips, desired duration, denominations → generates a blind structure to save as a block
- **Minimum player threshold** — configured in tournament metadata (typically 8-9), Start button unavailable until met
- **Chip bonuses** — configurable per tournament:
- Early signup bonus: bonus chips awarded to the first X players who buy in (configurable cutoff count)
- Punctuality bonus: bonus chips awarded to players who complete buy-in before the tournament starts (status transitions from 'registering' to 'running') — deterministic, automatic, no TD judgment call
- **Late registration soft lock with admin override** — when cutoff hits, registration locks but admin can push through a late entry (logged in audit trail). Some tournaments have no late reg at all (registration closes at start)
### Payout Structure
- **Entry-count brackets with tiered prizes** — standalone reusable building block, example:
- 820 entries → 3 prizes (50% / 30% / 20%)
- 2130 entries → 4 prizes (45% / 26% / 17% / 12%)
- 3140 entries → 5 prizes (42% / 26% / 16% / 11% / 5%)
- 4150 entries → 6 prizes (40% / 24% / 14% / 10% / 7% / 5%)
- **Entry count = unique entries only** — not rebuys or add-ons
- **Prize rounding** — all prizes round down to nearest venue-configured denomination (e.g. 50 DKK in Denmark, €5 elsewhere). No coins, no awkward change
- **Bubble prize** — happens in ~70% of tournaments. When remaining players exceed paid positions (e.g. 8 left, 7 paid), all remaining players can unanimously agree to create a bubble prize:
- Typically around the buy-in amount
- Funded by shaving from top prizes (mostly 1st3rd, sometimes 4th5th)
- TD flow: "Add bubble prize" → set amount → system shows redistribution across top prizes → confirm
- Must be fast and intuitive — not buried in menus, this happens constantly
### TD Workflow During Play
- **Overview tab priority** (top to bottom):
1. Clock & current level (biggest element)
2. Time to next break
3. Player count (registered / remaining / busted)
4. Table balance status (are tables even or needs attention)
5. Financial summary (prize pool, entries, rebuys)
6. Recent activity feed (last few actions)
- **Bust-out flow**: tap Bust → pick table → pick seat → system shows player name for verification → confirm → select hitman (mandatory in PKO tournaments, optional otherwise) → done
- **PKO (Progressive Knockout)**: when template defines PKO, bounty transfer is part of the bust flow (half to hitman, half added to their own bounty, chain tracked)
- **Buy-in flow**: search/select player → auto-seat suggests optimal seat → TD can override (accessibility needs, keep-apart situations like couples) → confirm → receipt
- **Multi-tournament switching**: TD chooses tabs at top OR split view — phone likely tabs, tablet landscape could do split view
- **Undo is critical** — bust-outs, rebuys, add-ons, buy-ins can all be undone with full re-ranking. Miscommunication between TD and dealers happens often
### Seating & Balancing
- **Oval table view (default)** — top-down view with numbered seats, player names in seats, empty seats visible. Switchable to list view for density (10+ table tournaments)
- **Balancing is TD-driven, system-assisted**:
1. System alerts: tables are unbalanced
2. TD requests suggestion: system says "move 1 from Table 1 to Table 4"
3. TD announces to the floor, assistant facilitates
4. Assistant reports back: "Seat 3 to Seat 5" — TD taps two seat numbers, done (minimal touches)
5. Suggestion is **live and adaptive** — if Table 1 loses a player before the move happens (e.g. bust during ongoing hand), system recalculates or cancels
- **Break Table is fully automatic** — system knows all open seats, distributes players evenly, TD just sees the result ("Player X → Table 1 Seat 4")
- **No drag-and-drop in Phase 1** — tap-tap flow for all moves
### Hand-for-Hand Mode
- Activated by TD when tournament reaches bubble (remaining players = paid positions + 1)
- Clock pauses, each table plays one hand at a time
- TD view: table grid showing completion status per table (completed / in progress)
- TD taps "Hand Complete" per table as dealers report
- When all tables complete: next hand starts automatically, or if someone busted the bubble is resolved
- If bust during hand-for-hand: check if bubble burst, if yes exit hand-for-hand mode and resume clock
- Hand-for-hand should be prominent (visible mode indicator on all clients) because players watch this closely
### End-Game & Payouts
- **Flexible chop/deal support** — all remaining players participate in a deal, nobody leaves while others play on. Supported scenarios:
- ICM calculation (TD inputs chip stacks, system calculates each player's share)
- Custom split (players agree on % or fixed amount)
- Partial chop (split some money, play on for remaining + league points)
- Any number of players (not just heads-up — 4-way deals after 12 hours are real)
- **Prize money and league positions are independent** — money can be chopped but positions still determined by play for points
- **Tournament auto-closes** when one player remains — no manual "end tournament" button
- **Receipts configurable per venue**: off / digital / print / both. Player and venue can always see tournament participation in the system regardless of receipt setting
### Claude's Discretion
- Loading skeleton and animation design
- Exact spacing, typography, and component sizing
- Error state handling and messaging
- Toast notification behavior and timing
- Activity feed formatting
- Thermal printer receipt layout
- Internal data structures and state management patterns
</decisions>
<specifics>
## Specific Ideas
- The bust-out flow must be as few taps as possible — TD is under time pressure during a running tournament
- Balancing recording should be two taps (source seat, destination seat) after the system suggests table-to-table
- Bubble prize creation needs to be fast and prominent — it happens in 70% of tournaments
- "Add bubble prize" should be easily accessible, not buried in settings
- Template building blocks should feel like LEGO — pick chip set, pick blind structure, pick payout table, name it, done
- The system should adapt to the chaos of live poker — hands in progress during balancing, players busting mid-move, deals negotiated at 2am after 12 hours of play
</specifics>
<code_context>
## Existing Code Insights
### Reusable Assets
- No existing code — greenfield project
### Established Patterns
- No established patterns yet — Phase 1 sets the foundation
### Integration Points
- Go binary serves SvelteKit via go:embed
- WebSocket hub for real-time state to all connected clients
- LibSQL for local persistence
- NATS JetStream for event durability
- Catppuccin Mocha dark theme (UI-05)
</code_context>
<deferred>
## Deferred Ideas
- **Drag-and-drop seat moves** — UI polish, add after core tap-tap flow works (future Phase 1 enhancement or later)
- **PWA seat move notifications** — when a player is moved (balancing or table break), notify them in the PWA with new table/seat — Phase 2
- **"Keep apart" player marking** — TD marks players who shouldn't be at the same table (couples, etc.), auto-seating respects it — evaluate for Phase 1 or later
</deferred>
---
*Phase: 01-tournament-engine*
*Context gathered: 2026-03-01*