Review: SvelteKit app: Keycloak + Stripe + game state

review-793-2026-04-03 Doc

review needs-refinement

Verdict: NEEDS_REFINEMENT

Template Completeness

  • [x] Type -- Feature
  • [x] Lineage -- references #4 (playground), #1 (infra), #3 (kustomize)
  • [x] Repo -- forgejo_admin/twitch-2k-wager
  • [x] User Story -- well-formed, covers end-to-end challenger flow
  • [x] Context -- thorough, lists all subsystems to wire up
  • [x] File Targets -- 12 files to create
  • [x] Acceptance Criteria -- 10 items
  • [x] Test Expectations -- 4 items
  • [x] Constraints -- no Tailwind, adapter-node, match playground
  • [x] Checklist -- standard
  • [x] Related -- lists all backing notes
All required feature template sections present. Template is complete.

Traceability

  • [x] story:challenger-auth -- verified: story-twitch-2k-wager-challenger-auth exists in pal-e-docs
  • [x] story:challenger-pay -- verified: story-twitch-2k-wager-challenger-pay exists in pal-e-docs
  • [x] story:game-status -- verified: story-twitch-2k-wager-game-status exists in pal-e-docs
  • [x] story:winner-payout -- verified: story-twitch-2k-wager-winner-payout exists in pal-e-docs
  • [x] story notes verified -- all 4 stories listed in project-twitch-2k-wager user-stories section
  • [x] arch:twitch-2k-wager-app -- label present. No exact arch note with slug "arch-twitch-2k-wager-app" exists, but arch-domain-twitch-2k-wager, arch-dataflow-twitch-2k-wager, and arch-deployment-twitch-2k-wager all exist and cover the app component. Acceptable -- the arch label refers to the app component described across the three architecture notes.
  • [x] arch notes verified -- all three architecture notes exist in pal-e-docs
  • [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#7, state: open
Traceability is complete. All four story notes and all three architecture notes verified.

File Targets

  • [x] All 12 files are creates in a repo that currently only has README.md -- no conflicts possible
  • [x] src/routes/+page.svelte -- landing page from playground
  • [x] src/routes/+layout.svelte -- app layout with auth state
  • [x] src/routes/game/[id]/+page.svelte -- game status page
  • [x] src/routes/auth/callback/+server.ts -- Keycloak OIDC callback
  • [x] src/routes/api/webhooks/stripe/+server.ts -- Stripe webhook handler
  • [x] src/routes/api/admin/result/+server.ts -- declare winner endpoint
  • [x] src/routes/api/checkout/+server.ts -- create Checkout session
  • [x] src/routes/payout/+page.svelte -- Connect onboarding redirect
  • [x] src/lib/db.ts -- Postgres connection + queries
  • [x] src/lib/stripe.ts -- Stripe client helpers
  • [x] Dockerfile -- production container
  • [x] svelte.config.js, package.json, vite.config.ts -- project config
File targets are valid. Greenfield repo -- all creates, parent directories will be created by SvelteKit scaffold.

Repo Placement

OK. Issue filed on forgejo_admin/twitch-2k-wager, which is the correct target repo for the SvelteKit app. Kustomize overlay work is correctly tracked as a separate ticket (#3) on pal-e-deployments.

Dependencies

  • #1 Service onboarding (needs_approval) -- BLOCKER. Keycloak realm + Twitch IdP + CNPG + funnel must exist before this app can function.
  • #3 Kustomize overlay (todo) -- BLOCKER for deployment. App can be built without it, but cannot deploy.
  • #2 Stripe spike (next_up) -- SOFT BLOCKER. Spike findings should inform Stripe Checkout + Connect implementation. Risk of rework if spike discovers issues with Connect Express deferred onboarding or webhook patterns. Not documented as a dependency in Lineage.
  • #4 Landing page prototype (needs_approval) -- Upstream. PR #13 merged. Playground HTML/CSS is the source for the SvelteKit promotion. Documented in Lineage.
Missing dependency: #2 (Stripe spike) is not listed in the Lineage section but its findings directly affect this ticket's Stripe implementation.

Acceptance Criteria

10 acceptance criteria covering 7 distinct subsystems:
  • Keycloak OIDC auth (AC 1)
  • Stripe Checkout session creation (AC 2)
  • Stripe webhooks confirm payment + update DB (AC 3)
  • Admin endpoint declares winner + triggers payout (AC 4)
  • Stripe Connect first-time onboarding (AC 5)
  • Stripe Connect repeat auto-transfer (AC 6)
  • Revenue split DB tracking (AC 7)
  • Email receipts to winner and loser (AC 8)
  • Mobile-first dark theme matches playground (AC 9)
  • Dockerfile builds and runs on port 3000 (AC 10)
Each AC is individually testable, but together they represent 7+ independent integration concerns. The Stripe Connect flow alone (deferred onboarding + account.updated webhook + transfer creation) is a multi-step integration. An agent cannot verify all of these in a single pass.

Blast Radius

Greenfield service -- no blast radius to existing services. The email sending pattern is new to this project; westside-contracts and basketball-api have their own email implementations that are unaffected. Rollback is trivial since this is a new service with no existing consumers.

Decomposition Assessment

NEEDS DECOMPOSITION -- route to skill-decompose-ticket.
Three-thing limit and five-minute rule assessment:
  • File targets: 12+ files -- exceeds the 3-file guideline by 4x
  • Acceptance criteria: 10 items -- exceeds the 5 AC guideline by 2x
  • Distinct subsystems: 7 (Keycloak OIDC, Stripe Checkout, Stripe Connect, webhooks, Postgres schema, admin API, email)
  • Estimated agent time: 30-60 minutes minimum -- 6-12x the 5-minute limit
  • Independent subtasks that could be parallelized: After scaffold + DB schema, Keycloak auth and Stripe Checkout are independent. After both, admin endpoint and game status page are independent.
Recommended decomposition into 6 sub-tickets:
  • SvelteKit scaffold + playground promotion -- package.json, svelte.config.js, vite.config.ts, Dockerfile, +layout.svelte, +page.svelte (from playground). Pure setup, no integrations. AC: builds, runs on port 3000, matches playground design.
  • Postgres schema + db.ts -- src/lib/db.ts with connection pool + all table creation (challengers, games, payments, payouts, revenue_splits). AC: tables created on startup, queries exported.
  • Keycloak OIDC auth flow -- src/routes/auth/callback/+server.ts, +layout.svelte auth state. AC: Twitch login via Keycloak works, session returned, username displayed.
  • Stripe Checkout + webhook -- src/routes/api/checkout/+server.ts, src/routes/api/webhooks/stripe/+server.ts, src/lib/stripe.ts. AC: three tiers create sessions, webhook confirms payment, game status updated in DB.
  • Admin result + payout flow -- src/routes/api/admin/result/+server.ts, src/routes/payout/+page.svelte. AC: admin declares winner, Connect onboarding for first-time winners, auto-transfer for repeat winners, revenue split when Marcus wins.
  • Game status page + email receipts -- src/routes/game/[id]/+page.svelte, email sending. AC: game status page shows state transitions, winner/loser emails sent.
Each sub-ticket targets 2-3 files, 1-2 AC, and fits within the 5-minute agent window. Sequential dependency chain: 1 then 2, then 3 and 4 in parallel, then 5, then 6.

Recommendation

  • [DECOMPOSE] 10 AC across 7 subsystems and 12+ files. Estimated 30-60 min agent time. Route to skill-decompose-ticket with the 6-ticket breakdown above.
  • [BODY] Add #2 (Stripe spike) to the Lineage section as a dependency: "Depends on #2 (Stripe spike findings)."