Review: Checkout API: guest flow (gamertag + email)
Verdict: READY
Fourth review pass (board item #813). All three recommendations from r3 have been addressed. Issue now has 11 source files + 6 test files = 17 total file targets, all verified in repo.
Previous Findings — Resolved
- [x]
[BODY]Addsrc/hooks.server.tsto File Targets — FIXED. Now listed under "Auth + routing" section with note: "relax PROTECTED_PREFIXES:/game/[id]must be accessible without auth (guest challengers have no session)./playalso unprotected." - [x]
[BODY]Addsrc/app.d.tsto File Targets — FIXED. Now listed under "Schema + types" section with note: "SessionData.twitchId becomes optional" - [x]
[BODY]Clarify AC #8 re: hooks.server.ts — FIXED. AC #8 now reads: "Game status page accessible without auth (hooks.server.ts PROTECTED_PREFIXES relaxed for /game and /play)" — explicitly names the file and the mechanism.
Template Completeness
- [x] Type — Feature
- [x] Lineage — Required by #38 and #40. Modifies existing checkout endpoint + schema.
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — clear role/want/so-that for guest challenger
- [x] Context — explains current twitch_id PK, full 6-step migration plan
- [x] File Targets — 11 source files + 6 test files = 17 total, organized by section
- [x] Acceptance Criteria — 11 testable criteria
- [x] Test Expectations — 4 expectations with CI gate
- [x] Constraints — backward compat, dedup keys, idempotent migration, no new deps
- [x] Checklist — PR + tests + no unrelated changes
- [x] Related — links #38, #40, story notes, arch note
Traceability
- [x] story:challenger-pay label — Secure Buy-In Payment
- [x] story note verified — found in project-twitch-2k-wager user-stories table (key: challenger-pay, note: story-twitch-2k-wager-challenger-pay, id 1158)
- [x] arch:dataflow-twitch-2k-wager label — Data Flow: Twitch 2K Wager
- [x] arch note verified — arch-dataflow-twitch-2k-wager note exists (id 1155, status: active)
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#39, open
File Targets
Schema + types (3 files — all verified):
- [x]
src/lib/schema.sql— verified: challenger.twitch_id is TEXT PRIMARY KEY (line 7), game.challenger_id is TEXT REFERENCES challenger(twitch_id) (line 21). Migration plan in Context section is accurate. - [x]
src/lib/types.ts— verified: Challenger interface has twitch_id: string (line 28), no numeric id field. Needs serial id + optional twitch_id + gamertag per issue. - [x]
src/app.d.ts— verified: imports SessionData from $lib/auth (line 1), Locals.session typed as SessionData | null (line 6). Type coherence will flow correctly when SessionData.twitchId becomes optional.
Core DB + API (3 files — all verified):
- [x]
src/lib/db.ts— verified: getChallenger takes twitchId: string (line 51), upsertChallenger ON CONFLICT (twitch_id) (line 66), createGame takes challengerId: string (line 96). All need INT id migration. - [x]
src/routes/api/checkout/+server.ts— verified: requires twitch_id in body (line 38), calls createGame(challenger.twitch_id) (line 59). Must accept gamertag+email without auth. - [x]
src/lib/stripe.ts— verified: createCheckoutSession metadata uses challenger_twitch_id string key (line 84). Must switch to challenger_id INT + gamertag.
Auth + routing (2 files — all verified):
- [x]
src/hooks.server.ts— verified: PROTECTED_PREFIXES = ['/game'] (line 13), redirects unauthenticated to /auth/login (line 42). Must relax for /game and /play per AC #8. - [x]
src/lib/auth.ts— verified: SessionData.twitchId is required string (line 181). Must become optional for guest flow.
Downstream consumers (3 files — all verified):
- [x]
src/routes/api/admin/result/+server.ts— verified: getChallenger(game.challenger_id) with TEXT type (line 101), updateConnectStatus uses challenger.twitch_id (line 169). Must use INT id. - [x]
src/routes/game/[id]/+page.server.ts— verified: ownership check session.twitchId !== game.challenger_id (line 26). Must skip for guests (no session). - [x]
src/routes/api/webhooks/stripe/+server.ts— verified: reads challenger_twitch_id from session metadata (line 112), queries challenger by twitch_id in handleAccountUpdated (line 143). Must use INT id from metadata.
Test files (6 — all verified):
- [x]
src/routes/api/checkout/__tests__/checkout.test.ts— exists - [x]
src/routes/api/admin/result/__tests__/result.test.ts— exists - [x]
src/routes/api/webhooks/stripe/__tests__/webhook.test.ts— exists - [x]
src/routes/game/[id]/__tests__/page-server.test.ts— exists - [x]
src/lib/__tests__/stripe.test.ts— exists - [x]
src/lib/__tests__/db.test.ts— exists
Blast radius coverage: 16 files contain
twitch_id references. 15 of 16 are covered by file targets. The 16th (src/routes/auth/callback/+server.ts) calls upsertChallenger with the same signature — backward compatible, correctly excluded.Repo Placement
OK. All 17 file targets are in forgejo_admin/twitch-2k-wager. Single-repo change. No cross-repo work needed.
Dependencies
- [x] #38 (/play guest checkout route) — depends on THIS ticket, in todo column. Not a blocker.
- [x] #40 (admin queue view) — depends on THIS ticket, in todo column. Not a blocker.
- [x] #11 (Stripe Checkout + webhook) — prerequisite, in validation column. Satisfied.
- [x] #9 (Postgres schema + db.ts) — prerequisite, done. Satisfied.
- [x] No unresolved upstream blockers.
Acceptance Criteria
11 AC are well-defined and testable by an agent:
- AC 1-6: API + DB assertions. Testable via unit tests and curl.
- AC 7: Admin result endpoint with INT challenger_id — testable via result.test.ts.
- AC 8: "Game status page accessible without auth (hooks.server.ts PROTECTED_PREFIXES relaxed for /game and /play)" — now explicitly names the file and mechanism. Verifiable.
- AC 9: Guest game status page skips ownership check — testable via page-server.test.ts.
- AC 10: Webhook handler uses INT challenger_id — testable via webhook.test.ts.
- AC 11: All test files updated and passing — testable via
npm test.
4 test expectations are clear and CI-verifiable.
Blast Radius
twitch_idappears in 16 files across src/. All covered or backward-compatible.- Auth callback is the only untargeted file — uses upsertChallenger with preserved signature. Safe.
- No other repos affected. #38 and #40 depend on this but are not yet implemented.
Decomposition Assessment
- 11 source files + 6 test files = 17 files in one repo.
- 11 AC + 4 test expectations = 15 verification points.
- All changes driven by a single schema migration — tightly coupled, sequential dependency chain.
- Estimated agent time: 5-8 minutes. Borderline but acceptable as single pass — changes are mechanical after schema/type updates.
- No decomposition needed.
Recommendation
No action needed. All prior findings resolved. Ticket is ready for dispatch.