fix: close 22 pre-demo bugs across auth, locks, races, and UX #1

Merged
TimCane merged 32 commits from features/pre-demo-improvements into main 2026-04-18 09:29:56 +01:00
TimCane commented 2026-04-16 18:09:40 +01:00 (Migrated from github.com)

Summary

22 bugs diagnosed in docs/bugs/ and fixed one commit each, grouped by cluster from docs/bugs/README.md.

Auth (4). New src/lib/session-token.ts primitive (32-byte random token, sha-256 hashed, timingSafeEqual compare) + Team.sessionTokenHash column. Scout lobby:join/rejoin bound to the token; /api/upload gated on it;
contentType whitelisted to image/webp; photoUrl validated against the app S3 prefix.

Round state (2). Game.round now bumps on game:newround, not game:start, so Team.round === Game.round during active play. Scout rejoin tolerates one round of lag.

Transactions (3). src/lib/game-mutex.ts per-game mutex + @@unique([gameId, round, name]) on Team. submission:submit, review:approve, and game:start each run as a single Prisma transaction.

Locks (4). review:approve/review:reject require lockedByLeader === leaderName. Lock-timeout map keyed on ${gameId}:${leaderName}. Startup sweep in registerSocketHandlers clears stale DB locks.

Silent failures (3). Client subscribes to server 'error' and toasts. rejoin:error triaged into fatal / recoverable / advisory. 0–500 ms jitter on scout rejoin after game:lobby.

Remaining (6). Duplicate pending rejected; game:start requires ≥2 teams; generateBoard backfills short template runs; socket reads game/team from socket.data, leaves prior rooms on join; pickRandomUnusedTeam;
PendingUpload sweep on endGame.

Test plan

  • npm run test — 51 passing (13 new: session-token, game-mutex, teams, game-logic)
  • npx tsc --noEmit clean
  • Manual: two teams + one leader, exercise join/rejoin, submit, lock/approve/reject, newround, endgame
  • Manual: restart server mid-review, confirm stale lock sweep on boot
  • Manual: submit via one team while another leader is approving the same square — confirm no orphan pending row
  • Manual: abandon an upload, endgame, confirm PendingUpload row + S3 object are swept

Notes

  • docs/bugs/*.md kept as-is (immutable diagnoses).
  • ESLint baseline: 29 pre-existing findings in dist/ + React-Compiler warnings; no new findings.
## Summary 22 bugs diagnosed in `docs/bugs/` and fixed one commit each, grouped by cluster from `docs/bugs/README.md`. **Auth (4).** New `src/lib/session-token.ts` primitive (32-byte random token, sha-256 hashed, `timingSafeEqual` compare) + `Team.sessionTokenHash` column. Scout `lobby:join`/`rejoin` bound to the token; `/api/upload` gated on it; `contentType` whitelisted to `image/webp`; `photoUrl` validated against the app S3 prefix. **Round state (2).** `Game.round` now bumps on `game:newround`, not `game:start`, so `Team.round === Game.round` during active play. Scout rejoin tolerates one round of lag. **Transactions (3).** `src/lib/game-mutex.ts` per-game mutex + `@@unique([gameId, round, name])` on Team. `submission:submit`, `review:approve`, and `game:start` each run as a single Prisma transaction. **Locks (4).** `review:approve`/`review:reject` require `lockedByLeader === leaderName`. Lock-timeout map keyed on `${gameId}:${leaderName}`. Startup sweep in `registerSocketHandlers` clears stale DB locks. **Silent failures (3).** Client subscribes to server `'error'` and toasts. `rejoin:error` triaged into fatal / recoverable / advisory. 0–500 ms jitter on scout rejoin after `game:lobby`. **Remaining (6).** Duplicate pending rejected; `game:start` requires ≥2 teams; `generateBoard` backfills short template runs; socket reads game/team from `socket.data`, leaves prior rooms on join; `pickRandomUnusedTeam`; `PendingUpload` sweep on `endGame`. ## Test plan - [x] `npm run test` — 51 passing (13 new: session-token, game-mutex, teams, game-logic) - [x] `npx tsc --noEmit` clean - [ ] Manual: two teams + one leader, exercise join/rejoin, submit, lock/approve/reject, newround, endgame - [ ] Manual: restart server mid-review, confirm stale lock sweep on boot - [ ] Manual: submit via one team while another leader is approving the same square — confirm no orphan pending row - [ ] Manual: abandon an upload, endgame, confirm `PendingUpload` row + S3 object are swept ## Notes - `docs/bugs/*.md` kept as-is (immutable diagnoses). - ESLint baseline: 29 pre-existing findings in `dist/` + React-Compiler warnings; no new findings.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
1st-Chertsey-Scout-Group/bingo!1
No description provided.