From 1d11d9becd34cf511f2e15261743f297b42ea67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Thu, 12 Mar 2026 14:05:42 +0100 Subject: [PATCH] fix spec review issues: add missing files, clarify DB migration, use Zod schemas Co-Authored-By: Claude Opus 4.6 --- .../specs/2026-03-12-issue1-fixes-design.md | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/docs/superpowers/specs/2026-03-12-issue1-fixes-design.md b/docs/superpowers/specs/2026-03-12-issue1-fixes-design.md index 5bd8daa..7669da2 100644 --- a/docs/superpowers/specs/2026-03-12-issue1-fixes-design.md +++ b/docs/superpowers/specs/2026-03-12-issue1-fixes-design.md @@ -8,21 +8,23 @@ ## 1. Entry Data Model -Replace the current country-only lineup with full ESC entries: +Replace the current country-only lineup with full ESC entries (shown as Zod schemas, matching codebase conventions): ```ts -type Entry = { - country: { code: string; name: string; flag: string } - artist: string - song: string -} +const entrySchema = z.object({ + country: z.object({ code: z.string(), name: z.string(), flag: z.string() }), + artist: z.string(), + song: z.string(), +}) -type Lineup = { - year: number - entries: Entry[] -} +const lineupSchema = z.object({ + year: z.number(), + entries: z.array(entrySchema), +}) ``` +Note: The existing `countrySchema` gains a `flag` field β€” this is a breaking change to the country object shape throughout the codebase. + The data file changes from `esc-2026.json` to `esc-2025.json` using real ESC 2025 entries for testing. Each entry includes the flag emoji in the data file. Display format everywhere: `πŸ‡©πŸ‡ͺ Abor β€” SΓΌden` @@ -58,8 +60,8 @@ type Prediction = { **Bottom section β€” scrollable entry list:** - All entries from the lineup - Already-assigned entries are dimmed/disabled -- Tapping an unassigned entry shows a small picker with only unfilled slots (1st/2nd/3rd/Last) -- After selecting a slot, the entry fills that slot and dims in the list +- Tapping an unassigned entry shows a popover with only unfilled slot options (1st/2nd/3rd/Last) +- After selecting a slot, the entry fills that slot, the popover closes, and the entry dims in the list **Submit button:** Appears when all 4 slots are filled. After submission or when predictions are locked, the UI becomes read-only showing assigned entries in their slots. @@ -87,11 +89,12 @@ Strip the entire Dish of the Nation feature: ## 5. Player List β€” Prediction Checkmark -Add prediction submission status to the player state broadcast: +Add prediction submission status to the game state broadcast: -- Server: When building room state or game state, include `hasSubmittedPrediction: boolean` per player by checking if a prediction exists in the GameManager -- Client: Player list component renders a checkmark icon (βœ“) next to player names that have submitted predictions -- Visible on all views (play, host, display) +- Shared: Add `predictionSubmitted: Map` (or equivalent) to `gameStateSchema` in `game-types.ts`. This lives on the game state, not the player schema, since it's game-specific data. +- Server: When building game state in `GameManager.getGameStateForPlayer()` / `getGameStateForDisplay()`, include which players have submitted predictions. +- Client: `player-list.tsx` reads from game state and renders a checkmark icon (βœ“) next to player names that have submitted predictions. +- Visible on all views (play, host, display). ## 6. Acts Naming @@ -113,6 +116,8 @@ Host control buttons: "Start Pre-Show", "Start Live Event", "Start Scoring", "En Predictions lock when advancing from Pre-Show to Live Event (previously act1 β†’ act2). +**DB migration:** The Postgres `actEnum` must be updated from `["lobby", "act1", "act2", "act3", "ended"]` to `["lobby", "pre-show", "live-event", "scoring", "ended"]`. Since there is no production data to preserve, drop and recreate the enum (via Drizzle `push` or a migration). The `predictions` table columns also change from `predictedWinner/top3/nulPointsPick` to `first/second/third/last` β€” same approach, drop and recreate. + ## 7. Lobby Code β€” Copy to Clipboard On the display view (and anywhere the room code is shown prominently): @@ -141,7 +146,7 @@ Predictions lock on `pre-show β†’ live-event` transition. ## Files Affected ### Modified -- `packages/shared/src/game-types.ts` β€” entry type, prediction model +- `packages/shared/src/game-types.ts` β€” entry/lineup schemas, prediction model, add predictionSubmitted to game state - `packages/shared/src/ws-messages.ts` β€” remove dish messages, update prediction message - `packages/shared/src/constants.ts` β€” act names - `packages/server/data/` β€” replace `esc-2026.json` with `esc-2025.json` @@ -149,12 +154,13 @@ Predictions lock on `pre-show β†’ live-event` transition. - `packages/server/src/games/game-service.ts` β€” remove dish persistence, update prediction columns - `packages/server/src/rooms/room-manager.ts` β€” act name references - `packages/server/src/ws/handler.ts` β€” remove dish handlers, update prediction handler -- `packages/server/src/db/schema.ts` β€” remove dish tables, update prediction columns +- `packages/server/src/db/schema.ts` β€” remove dish tables, update prediction columns, update actEnum values - `packages/server/tests/game-manager.test.ts` β€” rewrite for new model - `packages/server/tests/ws-handler.test.ts` β€” update for changed messages - `packages/client/src/stores/room-store.ts` β€” remove dish state, update game state shape - `packages/client/src/hooks/use-websocket.ts` β€” remove dish handlers - `packages/client/src/components/predictions-form.tsx` β€” rewrite as tap-to-assign +- `packages/client/src/components/player-list.tsx` β€” add prediction checkmark - `packages/client/src/routes/play.$roomCode.tsx` β€” remove dish UI - `packages/client/src/routes/host.$roomCode.tsx` β€” remove dish UI - `packages/client/src/routes/display.$roomCode.tsx` β€” remove dish UI, add copy-to-clipboard