Twitch 2K Wager
Notes
Doc 69
-
Validation: PlayMe2K Launch (18 items)
validation-playme2k-launch-2026-04-05Validation: PlayMe2K Launch — 2026-04-05
Environment
- URL: https://playme2k.tail5b443a.ts.net/
- Namespace: playme2k
- ArgoCD App: playme2k
- Repos: twitch-2k-wager, pal-e-deployments, pal-e-services, pal-e-platform
Evidence
Check Result Detail Production URL PASS curl returns HTTP 200 Pod status PASS twitch-2k-wager-7c78c96dc6-mtdlq — Running, 0 restarts Image tag PASS harbor.tail5b443a.ts.net/playme2k/app:d4d758f6660037aa6ab22786f803901a7cc3488d Woodpecker CI PASS Pipeline #33 (latest on main) — success. Last 5 pipelines all green. ArgoCD health PASS playme2k app — Healthy ArgoCD sync PARTIAL OutOfSync — Secret/harbor-creds references namespace twitch-2k-wager (should be playme2k). Non-blocking: app is healthy and serving. Dev overlay PASS playme2k-dev pod running, 0 restarts Services PASS twitch-2k-wager:3000 (prod), playme2k-dev:80 (dev overlay) Verdict
PASS — Application is live, healthy, serving traffic, zero restarts, all CI pipelines green. OutOfSync is a non-blocking namespace reference bug tracked separately.
Discovered Issues
- ArgoCD namespace mismatch: kustomize overlay references
twitch-2k-wagernamespace for harbor-creds Secret, but actual namespace isplayme2k. ArgoCD retried 5 times and failed. Needs fix in pal-e-deployments.
Items Validated (18)
From needs_approval (14):
- #1 Service onboarding: Keycloak + Twitch IdP + CNPG + funnel
- #2 Spike: Stripe Checkout + webhook + payout
- #3 Kustomize overlay (pal-e-deployments)
- #4 Landing page HTML/CSS prototype
- #14 Annotate playground for SvelteKit promotion
- #38 /play guest checkout route + landing updates
- #39 Checkout API: guest flow (gamertag + email)
- #40 Admin queue view for Marcus
- #45 Fix: Stripe redirect http→https
- #46 Fix: ADMIN_SECRET in dev overlay
- #47 Fix: Stripe webhook delivery for dev
- #54 Replace Connect with refund + payout portal
- #56 Fix: prod pod → playme2k live
- #57 Observability: blackbox probe + golden signals
From validation (4):
- #10 Keycloak OIDC auth (Twitch login)
- #11 Stripe Checkout + webhook
- #12 Stripe Connect payout + admin result
- #13 Game status UI + email receipts
-
Review: Challenge Links — Shareable Proposal URLs
review-852-2026-04-05Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — V2 ticket, depends on #67 and #69, part of #66
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — clear "As Marcus (or any player), I want to share a challenge link in Twitch chat"
- [x] Context — thorough two-scenario explanation (viewer initiates vs Marcus initiates)
- [x] File Targets — 4 files listed with annotations (UPDATE/NEW)
- [x] Acceptance Criteria — 6 testable conditions
- [x] Test Expectations — 5 test scenarios
- [x] Constraints — dependencies, nanoid length, open challenge optionality noted
- [x] Checklist — standard 3-item PR checklist
- [x] Related — links to #66, #67, #69, #70
All required sections present. Template is complete.
Traceability
- [x] story:player-propose label — "My $X to your $Y sent in under 5 seconds. Proposal visible to both sides immediately."
- [x] story note verified — found in project-twitch-2k-wager user-stories section
- [x] arch:twitch-2k-wager-app label — SvelteKit app component
- [ ] arch note MISSING — [SCOPE] Create architecture note arch-twitch-2k-wager-app for the SvelteKit frontend component
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#74, open
File Targets
- [ ]
src/routes/api/propose/+server.ts— ISSUE: marked UPDATE but file does NOT exist on main. This route is created by dependency #69 (proposal API). Ticket should clarify this is UPDATE after #69 lands, or change to NEW if #74 creates the initial route. - [x]
src/routes/challenge/[code]/+page.server.ts— NEW file, parent directory does not exist yet. Correct for new route. - [x]
src/routes/challenge/[code]/+page.svelte— NEW file. Correct for new route. - [ ]
src/lib/schema.sql— ISSUE: File exists but contains V1 schema (challenger/game model with fixed tiers). Noproposalstable exists. Addingchallenge_code TEXT UNIQUEto proposals table requires #67 (v2 schema) to land first. Ticket should note: "after #67 lands, proposals table will exist."
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, all file targets are in the same repo. Single-repo change. No multi-repo concerns.
Dependencies
- #67 (v2 schema — player pools, proposals) — board item 845, backlog, open. HARD DEPENDENCY: proposals table must exist before challenge_code column can be added.
- #69 (proposal API — propose, accept, counter, decline) — board item 847, backlog, open. HARD DEPENDENCY: /api/propose route must exist before this ticket can UPDATE it. /api/accept must exist for challenge page accept flow.
- #70 (dashboard shows challenge links) — board item 848, backlog. DOWNSTREAM: consumes challenge URLs generated by this ticket.
Dependencies are documented in the issue (Lineage + Constraints sections). Both hard dependencies are in backlog and must be completed first. Execution order: #67 then #69 then #74 then #70.
Acceptance Criteria
6 criteria, all testable by an agent:
- [x] "Every proposal generates a unique challenge_code and URL" — verifiable via API call + response check
- [x] "/challenge/[code] shows proposal terms to the right player" — verifiable via page load with auth
- [x] "Unauthenticated visitors redirected to login" — verifiable via unauthenticated request + redirect check
- [x] "Accept on challenge page calls /api/accept" — verifiable, but depends on #69 accept endpoint existing
- [x] "Invalid/expired challenge codes show friendly error" — verifiable via bad code request
- [x] "Open challenges (no to_player) can be claimed by first clicker" — verifiable, noted as optional in constraints
All criteria are specific and testable. No ambiguous language. Test expectations align with AC.
Blast Radius
Low. No existing challenge/invite/shareable-URL patterns in the codebase. This is a clean new feature addition. No sibling services affected. The /challenge route is a new namespace with no collision risk. Rollback is straightforward — remove the new route and revert the schema column addition.
Decomposition Assessment
4 file targets in 1 repo. 6 AC. 5 test expectations. All within one SvelteKit app. The work is: add a column to schema, modify one API route, create two new route files (server + page). Estimated agent time approximately 5 minutes. Borderline but acceptable as a single agent pass since all changes are tightly coupled and in one repo. No decomposition needed.
Recommendation
[BODY]Fix file target annotation:src/routes/api/propose/+server.tsis marked UPDATE but does not exist yet. Clarify: "UPDATE (after #69 lands creates this file)" so the executing agent understands the dependency.[BODY]Add note to schema file target: "After #67 lands, proposals table will exist — this ticket adds challenge_code column to that table."[SCOPE]Create architecture notearch-twitch-2k-wager-appfor the SvelteKit frontend component in pal-e-docs.
-
Review: Proposal API — Bidirectional Wager Proposals
review-847-2026-04-05Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — V2 core, depends on schema ticket, part of #66
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — player proposes "my $X to your $Y", other player accepts/counters/declines
- [x] Context — describes v1→v2 evolution clearly
- [x] File Targets — 4 new files, all under src/routes/api/
- [x] Acceptance Criteria — 7 criteria covering propose/accept/counter/decline/error/auth/atomicity
- [x] Test Expectations — 6 scenarios with specific amounts and expected outcomes
- [x] Constraints — transaction safety, amount bounds, auth, no UI
- [x] Checklist — standard 4-item
- [x] Related — references #66 parent, schema ticket, dashboard ticket
Traceability
- [x] story:player-propose — verified in project-twitch-2k-wager user-stories table ("My $X to your $Y sent in under 5 seconds. Proposal visible to both sides immediately.")
- [x] story note verified — found in project-twitch-2k-wager user-stories section
- [x] story:player-decide — verified in project-twitch-2k-wager user-stories table ("Accept, counter, or decline within the stream moment. Funds lock on accept.")
- [x] story note verified — found in project-twitch-2k-wager user-stories section
- [x] arch:dataflow-twitch-2k-wager — references the data flow architecture component
- [ ] arch note MISSING — [SCOPE] No note named arch-dataflow-twitch-2k-wager exists in pal-e-docs. The project page has an architecture section with a data model diagram, but no standalone arch note backs the label.
- [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/forgejo_admin/twitch-2k-wager/issues/69, state=open
File Targets
- [x] src/routes/api/propose/+server.ts — NEW file. Parent directory src/routes/api/ exists with sibling routes (admin, checkout, webhooks). Correct SvelteKit server route pattern.
- [x] src/routes/api/accept/+server.ts — NEW file. Same parent directory. Pattern consistent.
- [x] src/routes/api/counter/+server.ts — NEW file. Same parent directory. Pattern consistent.
- [x] src/routes/api/decline/+server.ts — NEW file. Same parent directory. Pattern consistent.
All 4 files are new API routes. Targets are specific enough for an agent to create without guessing. Each file target includes the expected request body, validation rules, and database operations.
Repo Placement
OK. Issue is filed on forgejo_admin/twitch-2k-wager, which is the correct repo (SvelteKit app + server routes). All file targets are in this repo. No cross-repo concerns — the ticket explicitly states "No UI" and "Dashboard ticket consumes these endpoints" (separate issue #70).
Dependencies
- [ ] Issue #67 (v2 schema — player pools, proposals, peer-to-peer game model) — PENDING. HARD BLOCKER. The proposal API imports db functions (createProposal, lockFunds, unlockFunds, etc.) and depends on tables (player_pools, proposals) that do not exist yet. Current schema has only challenger/game/payment/payout/revenue_split with fixed tiers. Board item #845 tracks #67, currently in backlog.
- [x] Issue #68 (deposit flow) — PENDING but NOT blocking. Players need deposits for pool balance, but proposal API can be built and tested with seeded pool data.
- [x] Issue #70 (dashboard frontend) — consumes these endpoints, does not block them.
Execution order: #67 (schema) must merge before #69 (this ticket) can be implemented.
Acceptance Criteria
All 7 criteria are testable and specific:
- [x] "Propose locks my_amount from proposer's pool, creates pending proposal" — verifiable via API call + DB state query
- [x] "Accept locks your_amount from accepter's pool, creates game" — verifiable via API call + DB state query
- [x] "Counter unlocks original, creates new proposal with swapped roles" — verifiable via sequential API calls + DB assertions
- [x] "Decline unlocks proposer's funds, no game" — verifiable via API call + DB state query
- [x] "Insufficient balance returns 400 with clear error" — verifiable via HTTP status check
- [x] "Only the to_player can accept/counter/decline (auth check)" — verifiable via 403 response with wrong session
- [x] "All pool operations are atomic (no partial locks on failure)" — verifiable via transaction rollback test
Test Expectations section adds 6 numeric scenarios with concrete amounts ($20/$10 with $50 balance, etc.). These are directly automatable as integration tests.
Blast Radius
- The proposal API does NOT modify any existing routes. /api/checkout and /api/admin/result remain unchanged.
- The 4 new routes import from src/lib/db.ts. Issue #67 (schema) adds the db functions these routes will call. No blast to existing db functions.
- Auth pattern: existing auth.ts uses Keycloak OIDC with PKCE. The new routes need the same pattern. This is consistent with existing route auth.
- No downstream consumers outside this repo. Rollback = delete the 4 new route directories.
Decomposition Assessment
Applying three-thing limit and five-minute rule:
- File count: 4 new files in 1 repo — borderline on three-thing limit but all follow identical pattern (validate request → check auth → check pool balance → mutate pool → update status → return response).
- AC count: 7 acceptance criteria — above the 5 threshold, but they test 4 variations of the same flow (propose/accept/counter/decline) plus 3 error cases.
- Estimated time: ~5 minutes. The routes share imports, auth pattern, and pool mutation logic. A single agent can implement all 4 as a cohesive unit.
- Parallelizable subtasks: No — the routes are interdependent (counter references propose, accept creates game from proposal). Splitting would create artificial seams.
No decomposition needed. The 4 routes form a single cohesive API surface and share enough internal structure that splitting would add overhead without reducing agent execution time.
Recommendation
- [SCOPE] Create architecture note
arch-dataflow-twitch-2k-wagerin pal-e-docs to back the arch:dataflow-twitch-2k-wager label. The project page has the data model but no standalone arch note. - [BODY] The "Related" section references "Schema ticket" and "Dashboard ticket" generically. Pin these to explicit issue numbers: "Schema ticket" → #67, "Dashboard ticket" → #70.
- [BODY] Add explicit dependency ordering note in the Constraints section: "BLOCKED by #67 (schema). Do not move to in_progress until #67 is merged."
-
Review: Admin Cleanup — Remove /admin, Everyone is a Player
review-851-2026-04-05Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — references parent #66
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — developer story, removing admin hierarchy
- [x] Context — explains dead code removal after V2 lands
- [x] File Targets — 5 targets listed
- [x] Acceptance Criteria — 6 criteria
- [x] Test Expectations — 3 items
- [x] Constraints — dependency ordering + cross-repo noted
- [x] Checklist
- [x] Related
Traceability
- [x] story:player-propose — present on board item, verified in project-twitch-2k-wager user-stories table
- [ ] arch:twitch-2k-wager-app — present on board item, but no backing architecture note found in pal-e-docs. [SCOPE] Create architecture note arch-twitch-2k-wager-app.
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#73, open
File Targets
- [x]
src/routes/admin/— verified: exists with +layout.server.ts, +page.server.ts, +page.svelte, __tests__/ - [x]
src/lib/admin-auth.ts— verified: exists (1.0k) - [x]
src/routes/api/admin/— verified: exists with result/ subdirectory and __tests__/ - [ ]
src/routes/+page.svelte— ISSUE: file exists but contains zero "admin" references. The AC "remove any /admin links from landing page" is a no-op. Target is inaccurate. - [x] pal-e-deployments overlay — verified: ADMIN_SECRET in
overlays/twitch-2k-wager/dev/deployment.yaml:46andoverlays/twitch-2k-wager/prod/deployment-patch.yaml:26 - [ ]
src/lib/db.ts— MISSING from file targets. Line 181 contains admin queue view comment. Needs cleanup. - [ ]
src/lib/types.ts— MISSING from file targets. Line 71 contains AdminQueueRow type definition. Needs cleanup.
Repo Placement
Cross-repo correctly identified. Primary work in forgejo_admin/twitch-2k-wager, secondary change in forgejo_admin/pal-e-deployments (remove ADMIN_SECRET from both dev and prod overlays). Issue body does not clarify whether the pal-e-deployments change is a separate PR/issue or included in this ticket's scope.
Dependencies
CRITICAL BLOCKER: Issue constraints state "ONLY do this after dashboard + result tickets are merged and validated."
- [ ] #70 (unified player dashboard) — OPEN, backlog column. Not started.
- [ ] #71 (result declaration) — OPEN, backlog column. Not started.
- [ ] Replacement
/api/resultroute — does not exist in codebase yet. - [ ] Replacement
/playdashboard — does not yet subsume admin queue functionality.
Dispatching this ticket now would delete admin routes with no replacement in place, breaking the platform for Marcus.
Acceptance Criteria
- [x] "/admin returns 404" — testable after deletion
- [x] "/api/admin/result returns 404" — testable after deletion
- [x] "admin-auth.ts deleted" — testable via file existence check
- [x] "ADMIN_SECRET env var removed from deployment" — testable, targets confirmed in both overlays
- [ ] "No references to 'admin' in codebase" — incomplete: db.ts and types.ts contain admin references not listed as file targets. Agent would miss these.
- [ ] "All functionality preserved via /play dashboard + /api/result" — untestable: /api/result does not exist yet, /play does not have admin features yet
Blast Radius
Moderate. Admin routes are self-contained (own layout, own auth via admin-auth.ts). No other services import admin-auth.ts. The db.ts query function and types.ts AdminQueueRow type are consumed only by admin pages — once admin is deleted, they become dead code. No downstream consumers outside this repo. Rollback is straightforward (revert PR).
Decomposition Assessment
Apply three-thing limit and five-minute rule:
- 2 repos (twitch-2k-wager + pal-e-deployments)
- ~7 file targets (5 listed + 2 discovered: db.ts, types.ts)
- 6 acceptance criteria + 3 test expectations
- All changes are deletion/removal — straightforward, no design decisions
On the edge of the three-thing limit but manageable in a single agent pass. The work is pure deletion with no new code to write. No decomposition needed — but the cross-repo pal-e-deployments change should be clarified (same PR or separate issue).
Recommendation
- [BODY] Add missing file targets:
src/lib/db.ts(remove admin queue query/comment at line 181) andsrc/lib/types.ts(remove AdminQueueRow type at line 71). - [BODY] Update or remove the file target for
src/routes/+page.svelte— no admin references exist in this file. The "remove /admin links" AC is already satisfied or was never needed. - [BODY] Clarify cross-repo strategy: will pal-e-deployments ADMIN_SECRET removal be a separate issue/PR or included in this ticket?
- [LABEL] Add
track:devopslabel since the ticket includes pal-e-deployments changes. - [SCOPE] Create architecture note
arch-twitch-2k-wager-appfor the SvelteKit app component. - [SCOPE] This ticket is blocked by #70 and #71. Do not move to next_up until both are merged and validated.
-
Review: V2 Schema — Pool, Proposal, Peer-to-Peer Models
review-845-2026-04-05Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- "Foundation for v2. All other v2 tickets depend on this. Part of #66."
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- present, well-formed (As a player...)
- [x] Context -- thorough, includes design decisions with dates
- [x] File Targets -- 3 files with detailed change descriptions
- [x] Acceptance Criteria -- 8 items
- [x] Test Expectations -- 5 items
- [x] Constraints -- 4 constraints, well-scoped
- [x] Checklist -- present
- [x] Related -- references parent #66, project page, downstream tickets #68-#73
Traceability
- [x] story:player-pool label -- "Any Player: Deposit once, bet instantly from pool. No Stripe redirect per game."
- [x] story note verified -- found in project-twitch-2k-wager user-stories table (key: player-pool)
- [x] arch:domain-twitch-2k-wager label -- present on board item
- [ ] arch note MISSING -- [SCOPE] No arch-domain-twitch-2k-wager note exists in pal-e-docs. The project page has an Architecture section with the data model, but no standalone arch-domain-* note backs this label. arch-dataflow-twitch-2k-wager exists (different component). Create architecture note arch-domain-twitch-2k-wager for the domain model component.
- [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/forgejo_admin/twitch-2k-wager/issues/67, open
File Targets
- [x] src/lib/schema.sql -- verified: exists (70 lines), contains current challenger/game/payment/payout/revenue_split tables. Ticket correctly identifies tables to modify/create (player_pools, proposals, game changes).
- [x] src/lib/types.ts -- verified: exists (93 lines), contains Challenger, Game, BuyInAmount, GameWithChallenger, Payment, Payout, RevenueSplit types. Ticket correctly identifies types to add (PlayerPool, Proposal) and modify (Game).
- [x] src/lib/db.ts -- verified: exists (306 lines), lazy pg.Pool, query helpers, CRUD for Challenger/Game/Payment/Payout/RevenueSplit. Ticket correctly identifies new functions needed (pool, proposal, updated game). Currently has no explicit transaction handling -- ticket's transaction requirement (BEGIN/COMMIT + SELECT...FOR UPDATE) is a net-new pattern for this codebase.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager. All 3 file targets are in that repo. Single repo change.
Dependencies
- [x] This is the foundation ticket -- all v2 tickets (#68 deposit, #69 proposals, #70 dashboard, #71 result, #72 withdrawal, #73 cleanup) depend on this. No upstream blockers.
- [x] Parent issue #66 (v2 vision) referenced in Lineage.
- [x] Board item #795 (twitch-2k-wager#9 "Postgres schema + db.ts") is in done -- the v1 schema this builds on.
- [x] No items in in_progress that would conflict. All 8 v2 items are in backlog.
Acceptance Criteria
- [x] AC 1-3 (table creation/modification) -- verifiable via schema inspection
- [x] AC 4 (migration safe for existing prod data) -- verifiable but requires prod-like data seed. Ticket mentions "26 games, multiple challengers" -- agent would need this data or a fixture.
- [ ] AC 5 (Marcus seeded) -- ambiguous. Ticket says "from his Keycloak twitch_id" but does not provide the concrete twitch_id value or a reference to where it lives (Keycloak admin API, secrets file, or hardcoded value). Agent cannot seed deterministically without this.
- [x] AC 6 (existing guests get pool records) -- verifiable via migration query
- [x] AC 7 (pool mutations atomic) -- verifiable via test expectations (concurrent proposals test)
- [x] AC 8 (backwards compatible) -- verifiable but broad. 22 files import from db/types. Agent needs to verify all existing callers still compile and pass.
Concern: AC 5 (Marcus seed) lacks the concrete twitch_id value. Agent will need to query Keycloak or a secrets file to get it.
Blast Radius
HIGH. 22 files across the codebase import from db.ts or types.ts. Key consumers:
- src/lib/stripe.ts -- references challenger_id in Stripe metadata
- src/routes/api/checkout/+server.ts -- uses BuyInAmount type, creates games with fixed tiers
- src/routes/api/webhooks/stripe/+server.ts -- references challenger_id in webhook metadata
- src/routes/api/admin/result/+server.ts -- uses getChallengerById, game.challenger_id
- src/routes/admin/+page.server.ts -- uses GameWithChallenger type, buy_in_amount
- 6 test files reference challenger_id, buy_in_amount, BuyInAmount
The ticket's "Keep existing functions working" constraint mitigates this, but the agent must not break existing function signatures or types. The removal of BuyInAmount tier CHECK (mentioned in File Targets) must be additive only -- existing callers pass BuyInAmount values that must remain valid. Rollback: reversible if migration is written with DROP IF EXISTS guards, but 22 downstream files make partial rollback risky.
Decomposition Assessment
NEEDS DECOMPOSITION.
- 3 file targets in 1 repo -- passes file/repo threshold
- 8 acceptance criteria + 5 test expectations = 13 total verification points -- exceeds the 5-AC ceiling
- Estimated agent work: 15-20 minutes. New tables, migration logic, ~10 new db functions, transaction patterns (new to codebase), type additions, backwards compatibility verification across 22 consumers.
- Independent subtasks identified: (1) schema.sql migration + new tables, (2) types.ts updates, (3) db.ts pool functions with transactions, (4) db.ts proposal + game functions, (5) migration test + backwards compatibility verification
[DECOMPOSE] 13 criteria across 3 files with new transaction patterns. Route to skill-decompose-ticket.
Recommendation
- [SCOPE] Create architecture note arch-domain-twitch-2k-wager for the domain model component (tables, types, db functions).
- [BODY] Add Marcus's twitch_id value (or a concrete reference: Keycloak admin API endpoint, secrets path, or env var) to AC 5 so the migration agent can seed deterministically.
- [DECOMPOSE] 13 criteria across 3 files with new transaction patterns. Route to skill-decompose-ticket for sub-board creation.
-
Review: Deposit Flow -- Stripe Checkout to Pool Balance
review-846-2026-04-05Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- V2 ticket, depends on schema ticket, part of #66
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- player deposits funds into pool via Stripe Checkout
- [x] Context -- explains V1 per-game payment vs V2 pool deposit model
- [x] File Targets -- 3 NEW + 1 UPDATE, all with descriptions
- [x] Acceptance Criteria -- 6 testable items
- [x] Test Expectations -- 3 items
- [x] Constraints -- 4 constraints including dependency, Stripe mode, metadata, backwards compat
- [x] Checklist -- standard 3-item
- [x] Related -- references #66 parent and schema dependency
Traceability
- [x] story:player-pool label -- present on board item #846
- [ ] story note MISSING -- project-twitch-2k-wager page has no user-stories section (page TOC is empty). [SCOPE] Create user-stories section on project-twitch-2k-wager with player-pool story entry.
- [x] arch:dataflow-twitch-2k-wager label -- present on board item #846
- [ ] arch note MISSING -- search_notes("arch-dataflow-twitch-2k-wager") returned zero results. [SCOPE] Create architecture note arch-dataflow-twitch-2k-wager for the data flow component.
- [x] Forgejo issue -- #68, open, well-formed
File Targets
- [x]
src/routes/deposit/+page.svelte-- NEW: deposit directory does not exist yet (expected for new feature). Parentsrc/routes/verified. - [x]
src/routes/deposit/+page.server.ts-- NEW: same directory as above (expected). - [x]
src/routes/api/deposit/+server.ts-- NEW:src/routes/api/exists (contains admin, checkout, webhooks). New deposit subdirectory expected. - [x]
src/routes/api/webhooks/stripe/+server.ts-- UPDATE: verified exists. Currently handlescheckout.session.completed(game payments via game_id metadata) andaccount.updated. Deposit flow needs metadata type check to distinguish deposit from game -- ticket correctly calls this out. - [ ]
src/lib/stripe.ts-- NOT LISTED but likely needs update. ExistingcreateCheckoutSessionis game-specific (takes game_id, tier, challenger details). Deposit flow will need either a new helper or modification. [BODY] Add as potential UPDATE target.
Repo Placement
OK. Issue filed on twitch-2k-wager, all file targets are in twitch-2k-wager. Single-repo change. No cross-repo concerns.
Dependencies
- BLOCKING: Issue #67 (v2 schema -- player_pools, proposals, peer-to-peer game model) is in backlog on board-twitch-2k-wager (board item #845). This ticket explicitly depends on the player_pools table. Verified:
db.tsandtypes.tshave zero pool/balance/deposit references. Schema must land first. - Issue #66 (parent vision -- peer-to-peer proposal flow) is open. This ticket is a child of that vision. Not a blocker.
- No other board items appear blocked by this ticket.
Acceptance Criteria
6 AC items -- all concrete and verifiable by an agent:
- [x] /deposit page renders with amount input and current balance -- testable via navigation + DOM check
- [x] Submitting creates Stripe Checkout session -- testable via API call verification
- [x] Webhook adds funds to pool balance -- testable via DB state check after webhook
- [x] Pool balance updates visible on page refresh -- testable via page reload + DOM check
- [x] $1 min / $500 max enforced -- testable via boundary inputs
- [x] Auth required -- testable via unauthenticated access attempt
All criteria are specific and programmatically verifiable. No ambiguous language.
Blast Radius
- Webhook handler update is the highest-risk change. Existing game payment flow must not break. Ticket correctly requires backwards compatibility via metadata type discriminator.
src/lib/stripe.tscreateCheckoutSessionis game-specific (takes game_id, tier, challenger metadata). Deposit flow will likely need a separate function or significant parameter changes. Not mentioned in file targets.- Rollback is straightforward: remove deposit route + revert webhook changes. No schema migration in this ticket (that is #67).
Decomposition Assessment
Apply the three-thing limit and five-minute rule:
- 4 file targets across 1 repo -- within limits
- 6 AC items -- borderline but tightly coupled to one deposit flow
- All changes serve a single user journey (deposit funds into pool)
- Frontend (deposit page) and backend (API + webhook) could theoretically be parallelized, but the tight coupling makes sequential execution simpler
- Estimated agent time: ~5 minutes -- at the boundary but acceptable for a cohesive feature
No decomposition needed.
Recommendation
- [SCOPE] Create user-stories section on project-twitch-2k-wager with player-pool story entry.
- [SCOPE] Create architecture note arch-dataflow-twitch-2k-wager for the data flow component.
- [BODY] Add
src/lib/stripe.tsas a potential UPDATE target -- the deposit checkout session likely needs a new helper or modification tocreateCheckoutSession. - [BODY] Replace vague "schema ticket" dependency reference with explicit
#67issue number in Constraints and Related sections.
-
Review: Mutual Result Submission
review-849-2026-04-05Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — V2 ticket, depends on #67, part of #66
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — player mutual confirmation
- [x] Context — clear migration from admin-declared to mutual agreement
- [x] File Targets — 5 files, all verified
- [x] Acceptance Criteria — 6 criteria
- [x] Test Expectations — 4 test cases
- [x] Constraints — dependencies, transition strategy noted
- [x] Checklist — standard
- [x] Related — parent #66 and dependency #67
Traceability
- [x] story:game-result label — mutual result submission story
- [ ] story note MISSING — [SCOPE] Create user story entry on project-twitch-2k-wager user-stories section (project page has no content)
- [x] arch:dataflow-twitch-2k-wager label — dataflow component
- [ ] arch note MISSING — [SCOPE] Create architecture note arch-dataflow-twitch-2k-wager
- [x] Forgejo issue — #71, open
File Targets
- [x]
src/lib/schema.sql— verified: exists, currently has no player_1_result/player_2_result columns (those depend on #67 schema migration) - [x]
src/lib/types.ts— verified: exists, Game interface present, needs result confirmation fields - [x]
src/lib/db.ts— verified: exists, has query helpers and game functions, no submitResult yet - [x]
src/routes/api/result/+server.ts— verified: does NOT exist (correctly marked NEW in ticket) - [x]
src/routes/game/[id]/+page.svelte— verified: exists, currently shows game status with polling, needs result buttons
Targets are specific enough for an agent to act on without guessing.
Repo Placement
OK — issue filed on forgejo_admin/twitch-2k-wager, all file targets are in the same repo. Single-repo change, no multi-repo concerns.
Dependencies
- [x] #67 (v2 schema) — PENDING. Open, in backlog (board item #845). This ticket requires player_1_result, player_2_result columns and the peer-to-peer game model from #67. Must merge first.
- [x] #66 (parent vision) — Open. Umbrella tracking issue. Not blocking.
Dependency on #67 is correctly documented in Lineage and Constraints. No unresolved surprises.
Acceptance Criteria
6 criteria, all testable by an agent:
- [x] Player result submission — API call + DB state check
- [x] Mutual agreement triggers pot transfer — API + DB verification
- [x] Disagreement sets disputed status — API + DB check
- [x] Non-participant auth check — 403 response assertion
- [x] Completed game guard — 400 response assertion
- [x] Waiting state UI — frontend state verification via polling
All criteria are specific and programmatically verifiable. No ambiguous language.
Blast Radius
- Existing admin result endpoint (
/api/admin/result) — ticket correctly notes keeping it during transition. Agent must not break it. - Game view page — currently handles won/lost/cancelled as terminal states. New "disputed" status needs handling in the isTerminal check and alert config in +page.svelte.
- Game status type — "disputed" must be added to the GameStatus union in types.ts and the CHECK constraint in schema.sql. Not explicitly called out in file targets but implied by AC.
- Page server (
game/[id]/+page.server.ts) — may need to load current user's result submission state so the frontend can show the correct buttons. Not listed in file targets.
Rollback is straightforward — revert migration, remove new endpoint. Existing admin flow untouched.
Decomposition Assessment
5 file targets in 1 repo. 6 acceptance criteria. 4 test expectations.
- Does the ticket have >3 discrete changes? Yes (5 files), but all are tightly coupled in a single request-response flow (schema → type → db → api → view). Splitting would create artificial boundaries.
- Would an agent need >5 minutes? Estimated 4-5 minutes. Borderline but manageable.
- Are there independent subtasks that could be parallelized? No — each layer depends on the one below it.
No decomposition needed. Single agent pass is appropriate.
Recommendation
[SCOPE]Create project page content for project-twitch-2k-wager with a user-stories section including the game-result story entry.[SCOPE]Create architecture note arch-dataflow-twitch-2k-wager for the dataflow component.[BODY]Addsrc/routes/game/[id]/+page.server.tsto File Targets — it likely needs to pass the current user's player ID so the frontend knows which result buttons to show and whether the user has already submitted.[BODY]Note in File Targets thatsrc/lib/types.tsalso needs "disputed" added to the GameStatus union type.
-
Review: Unified Player Dashboard (/play page)
review-848-2026-04-05Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- V2 ticket, part of #66
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- player wants unified dashboard with pool balance, proposals, games
- [x] Context -- explains V1 (guest checkout + admin queue) vs V2 (unified player dashboard) transition
- [x] File Targets -- 4 files listed with clear action verbs
- [x] Acceptance Criteria -- 9 criteria listed
- [x] Test Expectations -- 4 test cases
- [x] Constraints -- dependencies, mobile, stream visibility noted
- [x] Checklist -- standard PR/tests/no-unrelated
- [x] Related -- parent #66 and dependency tickets referenced
Traceability
- [x] story:player-propose -- verified in project-twitch-2k-wager user-stories table
- [x] story:player-decide -- verified in project-twitch-2k-wager user-stories table
- [ ] arch:twitch-2k-wager-app -- arch note MISSING in pal-e-docs. [SCOPE] Create architecture note arch-twitch-2k-wager-app for the SvelteKit frontend component.
- [x] Forgejo issue -- #70, open, forgejo_admin/twitch-2k-wager
File Targets
- [x] src/routes/play/+page.svelte -- verified: exists (5.4k, current V1 guest checkout page). REWRITE is correct.
- [ ] src/routes/play/+page.server.ts -- ISSUE: file does not exist on disk. Issue says "REWRITE" but this should be "CREATE." Parent directory src/routes/play/ exists. [BODY] Change "REWRITE" to "CREATE" for +page.server.ts.
- [x] src/routes/game/[id]/+page.svelte -- verified: exists. UPDATE is correct.
- [x] src/routes/game/[id]/+page.server.ts -- verified: exists. UPDATE is correct.
Targets are specific enough for an agent to act on without guessing.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager. All 4 file targets are in the same repo. No cross-repo work required. Single-repo ticket is correct.
Dependencies
- [ ] #67 (v2 schema -- player pools, proposals, peer-to-peer game model) -- PENDING. Open, backlog (board item #845). BLOCKER: dashboard queries pool/proposal/game tables that do not exist yet.
- [ ] #68 (deposit flow -- Stripe Checkout to fund player pool) -- PENDING. Open, backlog (board item #846). BLOCKER: dashboard shows pool balance which requires the deposit flow to exist.
- [ ] #69 (proposal API -- propose, accept, counter, decline) -- PENDING. Open, backlog (board item #847). BLOCKER: test expectations reference /api/propose and /api/accept endpoints that come from this ticket. Currently no /api/propose or /api/accept routes exist in the codebase.
- [x] #66 (parent vision) -- open. Epic umbrella, not a direct blocker.
All three blockers are in backlog. This ticket CANNOT move to in_progress until #67, #68, and #69 are merged.
Acceptance Criteria
9 acceptance criteria. Assessment:
- AC 1-7 are testable and specific -- an agent can verify pool balance display, form rendering, button presence, route behavior.
- AC 8 ("Requires authentication -- redirect if not logged in") is testable via server-side load function check.
- AC 9 ("Dark theme, mobile-friendly, stream-readable") is subjective and not agent-verifiable as written. [BODY] Refine to: "Uses existing dark theme CSS variables" (verifiable via grep) and "Mobile viewport (375px) renders without horizontal scroll" (verifiable via Playwright).
Test expectations reference /api/propose and /api/accept which do not exist yet (dependency on #69). Tests will fail until #69 merges -- this is expected and documented in Constraints.
Blast Radius
- The existing /admin route (admin-auth.ts, admin/+page.svelte, admin/+layout.server.ts, api/admin/result/) is extensive. AC says "No /admin link or reference anywhere" -- this means /play should not link to /admin, NOT that /admin is being removed. The /admin route cleanup is tracked separately (#73, board item #851).
- The /play route rewrite replaces the V1 guest checkout flow. Any external links pointing to /play with query params (tier selection) will break. This is expected for V2 but not called out in constraints.
- No downstream services affected -- this is a frontend-only rewrite consuming new API endpoints.
- Rollback is straightforward -- revert the PR to restore V1 /play.
Decomposition Assessment
Apply three-thing limit and five-minute rule:
- File count: 4 files in 1 repo. Two are rewrites/creates (play route), two are updates (game route). Under the 3-file-across-2-repos threshold.
- AC count: 9 acceptance criteria. Exceeds the 5 AC threshold.
- Estimated agent time: 5-8 minutes. Borderline on the 5-minute rule.
- Parallelization: The /play rewrite and /game/[id] update are logically independent -- they could be split into two tickets. However, they share the same proposal data model and visual language, making a single cohesive pass more efficient than two context-switching passes.
No decomposition needed. The 9 AC count is a warning flag, but the work is cohesive within a single route family. If agent exceeds 5 minutes during execution, split /game/[id] updates into a follow-up ticket.
Recommendation
- [BODY] Fix file target: src/routes/play/+page.server.ts -- change "REWRITE" to "CREATE" (file does not exist on disk)
- [SCOPE] Create architecture note arch-twitch-2k-wager-app for the SvelteKit frontend component in pal-e-docs
- [BODY] Refine AC 9 "Dark theme, mobile-friendly, stream-readable" into agent-verifiable criteria: "Uses existing dark theme CSS variables" and "Mobile viewport (375px) renders without horizontal scroll"
-
Review: Withdrawal Flow — Pool to Bank via Stripe Connect
review-850-2026-04-05Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — "V2 ticket. Depends on schema ticket. Part of #66."
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — player with winnings withdrawing to bank
- [x] Context — explains current vs v2 approach
- [x] File Targets — 1 new file specified
- [x] Acceptance Criteria — 5 criteria
- [x] Test Expectations — 3 test cases
- [x] Constraints — dependencies and limits noted
- [x] Checklist — standard PR checklist
- [x] Related — parent vision #66, schema dependency
Traceability
- [x] story:player-withdraw label — verified on board item #850
- [x] story note verified — "player-withdraw" entry found in project-twitch-2k-wager user-stories table: "Withdraw pool balance to bank via Stripe payout anytime."
- [x] arch:dataflow-twitch-2k-wager label — present on board item
- [ ] arch note MISSING — [SCOPE] No note matching "arch-dataflow-twitch-2k-wager" or "dataflow-twitch-2k-wager" found in pal-e-docs. Create architecture note arch-dataflow-twitch-2k-wager for the data flow component.
- [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/forgejo_admin/twitch-2k-wager/issues/72, state: open
File Targets
- [x]
src/routes/api/withdraw/+server.ts— marked NEW. Confirmed directory does not exist in repo yet (correct). Parent pathsrc/routes/api/exists with sibling routes (checkout, admin, webhooks). SvelteKit route convention is valid. - [x] Dashboard withdraw button — correctly deferred to separate ticket (good decomposition).
Targets are specific enough for an agent to act on without guessing.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, file target is in same repo. Single-repo change. No cross-repo concerns.
Dependencies
- [ ] Schema ticket #67 — PENDING (backlog). The current schema has no pool/balance tables (
player_pool,balance,lockedcolumns). The withdrawal endpoint needs these v2 schema objects. Board item #845 is in backlog column — not yet reviewed or in progress. This is a hard blocker. - [x] Parent vision #66 — open, serves as tracking issue. Not a blocker.
Acceptance Criteria
- [x] "Player can request withdrawal of any amount up to available balance" — testable via API call + DB assertion
- [x] "Locked funds cannot be withdrawn" — testable with locked balance fixture
- [x] "Stripe payout initiated successfully" — testable with Stripe mock
- [x] "Pool balance reduced by withdrawal amount" — testable via DB assertion
- [x] "Insufficient balance returns 400" — testable via API call
- Missing: No auth guard criterion — unauthenticated requests should return 401. All other API routes in this repo enforce auth.
- Missing: No mention of withdrawal record tracking — should the system maintain an audit trail of withdrawals? The v2 schema (#67) may need a withdrawals table.
Blast Radius
- Files touched: 1 new file. Low blast radius.
- Stripe mechanism ambiguity: Ticket title says "Stripe Connect" but the project page architecture says "Stripe payout from balance." Current codebase (
src/lib/stripe.ts) uses Connect Express transfers viacreateTransfer()which requires a connected account per recipient. Issue #54 (done) already replaced the original Connect flow with "refund + payout portal." The v2 withdrawal flow needs to specify which Stripe API: Connect transfer (existing pattern, requires per-recipient onboarding) vs. standard Stripe payout (different API, different requirements). This ambiguity would cause an agent to guess. - Rollback: Straightforward — single new file, no schema migration in this ticket.
Decomposition Assessment
Apply three-thing limit and five-minute rule:
- 1 new file — under the 3-file limit
- 5 AC — at the boundary but all within a single endpoint
- Single repo, single route — estimated agent time well under 5 minutes
- No independent subtasks that could be parallelized
No decomposition needed.
Recommendation
- [BODY] Replace "Depends on schema ticket" with "Depends on #67 (v2 schema)" in Lineage section. Add
#67explicitly to Related section. - [BODY] Clarify Stripe mechanism in Context section: specify whether withdrawal uses Connect Express transfer (existing
createTransfer()pattern, requires connected account onboarding) or a different Stripe payout method. Resolve contradiction between title ("Stripe Connect") and project vision ("standard Stripe payouts"). - [BODY] Add AC: "Unauthenticated request returns 401."
- [BODY] Add AC or Constraint: specify whether a
withdrawalstable is needed for audit trail, or confirm this is handled by the schema ticket #67. - [SCOPE] Create architecture note
arch-dataflow-twitch-2k-wagerfor the data flow component. This note is referenced by 5 board items on board-twitch-2k-wager (items #846, #847, #849, #850, and others).
-
Review: feat: custom even-money wager amounts ($1-$500)
review-841-2026-04-05-r2Verdict: APPROVED
Re-review after refinement. All 6 findings from
review-841-2026-04-05have been addressed. Scope is solid, file targets verified, traceability complete.Template Completeness
- [x] Type — Feature
- [x] Lineage — Phase 1 of #44 (custom wager amounts)
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — challenger wants custom wager amount instead of fixed tiers
- [x] Context — clear motivation, even-money constraint stated, fixed presets remain
- [x] File Targets — 7 files listed with specific change descriptions
- [x] Acceptance Criteria — 7 testable criteria
- [x] Test Expectations — 4 test cases with specific dollar values and expected behavior
- [x] Constraints — 5 constraints including Stripe minimum, $500 max, which CHECK to preserve
- [x] Checklist — standard 3 items
- [x] Related — parent issue #44, story note, arch note all referenced
Traceability
- [x] story:challenger-pay — "Secure Buy-In Payment" (
story-twitch-2k-wager-challenger-payexists in pal-e-docs) - [x] story note verified — found in
project-twitch-2k-wageruser-stories table (key: challenger-pay) - [x] arch:domain-twitch-2k-wager — "Domain Model: Twitch 2K Wager" (
arch-domain-twitch-2k-wagerexists in pal-e-docs) - [x] arch note verified — architecture note, active status, documents Game entity with buy_in_amount field
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#64, open
File Targets
- [x]
src/routes/play/+page.svelte— verified: has tier pills (lines 59-70), needs "Custom" option added alongside existing presets - [x]
src/lib/stripe.ts— verified: hasTIERSlookup (line 31-36),VALID_TIERS(line 45),createCheckoutSessionrequiresTierDollarstype (line 56). All need widening for custom amounts. - [x]
src/lib/schema.sql— verified: line 28 hasCHECK (buy_in_amount IN (100, 1500, 3000, 4500))to REMOVE. Line 30 hasCHECK (pot_amount = buy_in_amount * 2)to PRESERVE. Ticket correctly distinguishes between the two. - [x]
src/lib/types.ts— verified: line 24 hasexport type BuyInAmount = 100 | 1500 | 3000 | 4500;— needs widening tonumberwith runtime validation - [x]
src/lib/db.ts— verified: line 132 hasbuyInAmount: Game['buy_in_amount']derived fromBuyInAmountunion type. Coupled to enum; needs updating when type widens. - [x]
src/routes/api/checkout/+server.ts— verified: validates viaVALID_TIERS.includes()(line 45), casts throughTIERS[tierDollars].amount(line 72). Needs range validation ($1-$500) instead of tier lookup. - [x]
src/routes/api/webhooks/stripe/+server.ts— verified: line 111 readssession.metadata?.tierfor logging.handleCheckoutCompletedreadsgame_idfrom metadata (line 73) which is tier-agnostic. Ticket correctly uses "evaluate if" language for this target.
Repo Placement
OK. All 7 files are in
forgejo_admin/twitch-2k-wager. Issue is filed on the same repo. Single-repo change, no cross-repo concerns.Dependencies
- [x] No blocking dependencies. Parent #44 is a tracking issue (Phase 1 + Phase 2), not a blocker.
- [x] Board item #832 (winner payout, #60) — independent, touches
arch:dataflow-twitch-2k-wager, no file overlap. - [x] Board item #842 (#65, asymmetric odds) — Phase 2 of #44, sequenced after this ticket. No conflict.
- [x] Production DB migration — removing CHECK constraint requires ALTER TABLE on live DB. Standard practice for this project. Not a code dependency.
Acceptance Criteria
All 7 AC are agent-verifiable:
- [x] AC 1 ("Custom" option on /play) — verifiable via DOM inspection or screenshot
- [x] AC 2 (any $1-$500 creates valid session) — verifiable via checkout API POST with custom amount
- [x] AC 3 (pot always 2x) — verifiable via DB query after checkout, pot_amount CHECK constraint preserved in schema
- [x] AC 4 (fixed presets still work) — verifiable via existing tier checkout tests
- [x] AC 5 (admin shows custom amounts) — admin
tierLabel()already returns${cents/100}for non-standard amounts; safe - [x] AC 6 (input validation) — verifiable via API: reject decimals, amounts below $1 or above $500
- [x] AC 7 (webhook processes custom amounts) — verifiable via webhook test with custom amount metadata
Blast Radius
- Admin page (
src/routes/admin/+page.svelte):tierLabel()already has fallback for non-standard amounts (${cents/100}).tierClass()returns empty string — no color badge for custom. Acceptable, not a blocker. - Game detail page (
src/routes/game/[id]/+page.svelte):getTierLabel()already returns'Custom'for default case. Safe. - Landing page (
src/routes/+page.svelte): has hardcoded tier cards (lines 97-185). Display-only, not a checkout entry point. Not affected. - Existing tests: 3 test suites reference fixed tiers (
checkout.test.ts,stripe.test.ts,db.test.ts). Will need updating, implied by AC "Tests pass." - DB migration: removing CHECK constraint on live table requires
ALTER TABLE game DROP CONSTRAINT. Schema.sql is truth for new deploys; ALTER needed for production. - Rollback: straightforward — re-add the CHECK constraint, revert type changes. Custom-amount games already in DB would violate the constraint, so rollback would need to handle or exclude them.
Decomposition Assessment
7 file targets in 1 repo. 7 AC. 4 test expectations. Borderline on the three-thing limit by raw count, but the changes form a single tightly-coupled dependency chain:
types.ts(widen type) →db.ts(accept new type) →schema.sql(drop CHECK) →stripe.ts(accept any amount) →checkout/+server.ts(range validation) →play/+page.svelte(Custom UI) +webhooks/stripe/+server.ts(evaluate metadata)Splitting this chain would create integration risk — each piece is meaningless without the others. Estimated agent time: 3-5 minutes. No decomposition needed.
Recommendation
No action needed. Ticket is ready for dispatch.
Refinement Verification (6/6 addressed)
- [x] Project page —
project-twitch-2k-wagernow exists in pal-e-docs - [x] Architecture notes —
arch-domain-twitch-2k-wagerexists in pal-e-docs - [x] Label mismatch — board label corrected to
arch:domain-twitch-2k-wager - [x] Missing db.ts target — added to File Targets with createGame type coupling description
- [x] CHECK constraint clarity — issue explicitly distinguishes which CHECK to remove vs preserve
- [x] Webhook handler — added to File Targets with "evaluate if" language
-
Review: Win/lose email notifications via Gmail (re-review)
review-832-2026-04-05-r2Verdict: APPROVED
RE-REVIEW of board item #832. Previous review identified the need to specify
googleapisnpm package and make cross-repo deployment dependency explicit. Both refinements are now present in the issue body. Scope is solid and ready for agent dispatch.Template Completeness
- [x] Type -- Feature
- [x] Lineage -- winner-payout story
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- present with standard As/I want/So that
- [x] Context -- thorough: identifies stubs, specifies googleapis not gmail-sdk, names dedicated Gmail account
- [x] File Targets -- 4 targets across 2 repos, all specific
- [x] Acceptance Criteria -- 4 criteria, all testable
- [x] Test Expectations -- 3 expectations with concrete verification
- [x] Constraints -- 4 constraints including cross-repo flag and email approval gate
- [x] Checklist -- 6 items including both PRs
- [x] Related -- references story slugs and related issue #65
Traceability
- [x] story:winner-payout label -- present on board item
- [ ] story note NOT VERIFIED -- no project-twitch-2k-wager project page exists in pal-e-docs. User stories section cannot be checked. [SCOPE] Create project page project-twitch-2k-wager with user-stories section.
- [x] arch:dataflow-twitch-2k-wager label -- present on board item
- [ ] arch note NOT VERIFIED -- search for arch-dataflow-twitch-2k-wager returned no results. [SCOPE] Create architecture note arch-dataflow-twitch-2k-wager. (Note: this same gap has been flagged on multiple prior reviews for this board -- it is a known project-level gap, not a blocker for this individual ticket.)
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#60, state: open
File Targets
- [x]
package.json-- verified: exists, currently has pg + stripe deps. googleapis not yet present (expected, this is the addition). - [x]
src/lib/email.ts-- verified: exists, 185 lines. Line 156 has// TODO: Wire up Gmail OAuth transportexactly as described. sendWinnerEmail() and sendLoserEmail() are exported stubs that log in dev mode and return error when EMAIL_ENABLED=true. HTML templates already exist. - [x]
src/routes/api/admin/result/+server.ts-- verified: exists. Currently does NOT import email functions (expected). handleChallengerWin and handleMarcusWin are the correct insertion points for email calls after status transitions. - [x]
overlays/twitch-2k-wager/prod/deployment-patch.yaml(pal-e-deployments) -- verified: exists at ~/pal-e-deployments/overlays/twitch-2k-wager/prod/deployment-patch.yaml. Currently has DATABASE_URL, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, ADMIN_SECRET, KEYCLOAK_* env vars. Gmail env vars not yet present (expected).
Repo Placement
Correct. Issue is filed on forgejo_admin/twitch-2k-wager which is the primary repo. Cross-repo dependency on pal-e-deployments is explicitly called out in File Targets, Constraints, and Checklist (separate PR required). Two PRs needed: one for twitch-2k-wager, one for pal-e-deployments.
Dependencies
- [x] Prerequisites section documents 4 manual blocking steps (Gmail account creation, OAuth app, refresh token, K8s secret). Correctly flagged as non-automatable. Agent cannot complete this ticket until Lucas completes the prerequisites.
- [x] Board item #842 (issue #65, "asymmetric odds") also references email and shares the arch:dataflow-twitch-2k-wager label. The Related section in #60 correctly references #65 as a downstream consumer.
- [x] No blockers in in_progress -- all board items are in backlog or done.
Acceptance Criteria
4 criteria, all verifiable:
- "Winner gets email with payout claim link" -- verifiable in dev mode via console logs; real verification requires manual Gmail check per test expectations.
- "Loser gets email with thanks message" -- same as above.
- "Emails send via Gmail OAuth (googleapis npm package)" -- verifiable by code inspection + unit tests.
- "Test emails to draneylucas@gmail.com verified on phone" -- requires Lucas manual verification. Correctly flagged in test expectations and aligns with feedback_never_email_without_approval.
Criteria are complete and testable. The manual verification gate is appropriate.
Blast Radius
Low. Email module is self-contained in
src/lib/email.ts. Only one test file (src/lib/__tests__/email.test.ts) covers the module. The result endpoint modification adds email calls but does not change existing Stripe/payout logic. No other services in the platform use the googleapis npm package for email (basketball-api uses Python gmail-sdk). No cross-service impact. Rollback is straightforward (revert the email calls, remove env vars).Decomposition Assessment
4 file targets across 2 repos, 4 acceptance criteria. Borderline but acceptable as a single agent pass:
- The twitch-2k-wager changes are tightly coupled (email.ts transport + result endpoint import = one logical change).
- The pal-e-deployments change is 3 env var additions to an existing patch file (trivial).
- No independent subtasks that would benefit from parallelization -- the email transport must be wired before the endpoint can call it.
- Estimated agent time: 3-4 minutes. Under the 5-minute threshold.
No decomposition needed. The two-PR requirement is inherent to cross-repo work, not a decomposition signal.
Recommendation
No blocking issues. Two [SCOPE] items are project-level gaps that pre-date this ticket and do not block dispatch:
- [SCOPE] Create project page
project-twitch-2k-wagerwith user-stories section (project-level gap). - [SCOPE] Create architecture note
arch-dataflow-twitch-2k-wager(project-level gap, flagged on multiple prior reviews).
No [BODY], [LABEL], or [DECOMPOSE] recommendations. Issue body is well-refined after the first review cycle.
-
Review: Win/lose email notifications via Gmail
review-832-2026-04-05Verdict: NEEDS_REFINEMENT
Board item #832 — Forgejo issue
forgejo_admin/twitch-2k-wager#60Template Completeness
- [x] Type — Feature
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] Prerequisites
- [x] File Targets
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
All template sections present and populated.
Traceability
- [x] story:winner-payout label — present on board item
- [ ] story note MISSING —
project-twitch-2k-wagerhas empty TOC, no user-stories section. [SCOPE] Create user story entries on project-twitch-2k-wager user-stories section. - [x] arch:dataflow-twitch-2k-wager label — present on board item
- [ ] arch note MISSING — search for "dataflow-twitch-2k-wager" returned zero results in pal-e-docs. [SCOPE] Create architecture note
arch-dataflow-twitch-2k-wagerfor the dataflow component. - [x] Forgejo issue —
forgejo_admin/twitch-2k-wager#60, open
File Targets
- [x]
src/lib/email.ts— verified: sendWinnerEmail() and sendLoserEmail() stubs exist (lines 178, 197). sendEmail() has TODO at line 156 for Gmail OAuth transport. HTML templates already built. - [x]
src/routes/api/admin/result/+server.ts— verified: does NOT currently import or call email functions. handleChallengerWin() (line 109) and handleMarcusWin() (line 181) are the correct integration points. - [x] K8s secret
twitch-2k-wager-secrets— verified: exists in pal-e-deployments overlays (dev + prod). No Gmail env vars present yet.
Targets are specific and actionable.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, primary code changes target same repo. K8s secret lives in pal-e-deployments but is an ops prerequisite (manual step by Lucas), not agent code work. Single-repo ticket is appropriate.
Dependencies
- [ ] Gmail account creation — pending (Lucas manual step). ~/secrets/playme2k/ exists but contains only Stripe and Twitch credentials, no Gmail tokens.
- [ ] OAuth app in Google Cloud Console — pending (Lucas manual step)
- [ ] OAuth tokens stored in ~/secrets/playme2k/ — pending
- [ ] K8s secret updated with Gmail credentials — pending
- [x] email.ts stubs — satisfied (code exists)
- [x] result endpoint — satisfied (code exists, ready for integration)
All 4 prerequisites are manual human tasks. Agent cannot execute until Lucas completes them. This is correctly documented in the issue.
Acceptance Criteria
- [x] "Winner gets email with payout claim link" — testable once Gmail is wired
- [x] "Loser gets email with receipt/thanks" — testable once Gmail is wired
- [x] "Emails send via Gmail OAuth (dedicated PlayMe2K account)" — testable
- [x] "Test emails to draneylucas@gmail.com verified on phone" — testable, follows feedback_test_email_address convention
ISSUE: The ticket says "wire up gmail-sdk" but
gmail-sdk-ldraneyis a Python package (used in basketball-api). This is a SvelteKit/TypeScript project with no JS equivalent. The ticket must specify the actual Node.js Gmail API approach (e.g.,googleapisnpm package or direct REST with OAuth2 bearer tokens). Without this, an agent will waste time looking for a nonexistent JS package.Blast Radius
- 2 files modified in twitch-2k-wager repo
- Email is per-project per convention (feedback_email_architecture) — no shared email service affected
- basketball-api uses completely different stack (Python + gmail-sdk-ldraney) — no cross-contamination
- Rollback: revert PR + set EMAIL_ENABLED=false. Straightforward.
Decomposition Assessment
Apply the three-thing limit and five-minute rule:
- 2 file targets in 1 repo — under threshold
- 4 acceptance criteria — under threshold
- Estimated agent work: ~3-4 minutes (wire OAuth transport in email.ts, add email calls to result endpoint)
- No independent subtasks that need parallelization
No decomposition needed.
Recommendation
- [BODY] Replace "gmail-sdk" references in Context and Constraints sections with the actual Node.js approach.
gmail-sdk-ldraneyis Python-only. Specifygoogleapisnpm package or direct Gmail REST API with OAuth2 bearer tokens. - [SCOPE] Create user-stories section on
project-twitch-2k-wagerwith winner-payout story entry. - [SCOPE] Create architecture note
arch-dataflow-twitch-2k-wagerfor the dataflow component.
-
Validation: PlayMe2K #63
validation-63-2026-04-05Validation: #63 ADMIN_SECRET in prod
Verdict: PASS
PR #96 (pal-e-deployments) merged. /admin returns 401 (auth required) instead of 500 (not configured). ADMIN_SECRET env var confirmed in deployment spec.
-
Validation: PlayMe2K #58
validation-58-2026-04-05Validation: #58 /terms page
Verdict: PASS
PR #62 merged. Pipeline #37 green. /terms returns 200. /play no longer shows steps. Pod running image 420d2d7, 0 restarts.
-
Validation: PlayMe2K #59
validation-59-2026-04-05Validation: #59 vite allowedHosts
Verdict: PASS
PR #61 merged. Pipeline #37 green. Pod running image 420d2d7, 0 restarts. All routes: / 200, /play 200, /admin 401, /terms 200.
-
Review: fix: /admin returns 500 — ADMIN_SECRET not in prod deployment
review-835-2026-04-05Verdict: READY
Template Completeness
- [x] Type — Bug
- [x] Lineage — Standalone, discovered scope from #46
- [x] Repo — forgejo_admin/pal-e-deployments
- [x] User Story — Marcus (operator) needs /admin to load so he can manage the game queue
- [x] Context — ADMIN_SECRET added to dev overlay in #46 but never to prod
- [x] File Targets — 1 file with exact YAML block to add
- [x] Acceptance Criteria — 4 testable criteria
- [x] Test Expectations — kubectl verify, curl /admin, ArgoCD sync check
- [x] Constraints — single-file change, no new secrets needed
- [x] Checklist — implicit in file targets (single block addition)
- [x] Related — links to #46 and #95
Traceability
- [x] story:operator-flow label — Marcus's operator view
- [ ] story note MISSING — [SCOPE] Create user story entry "operator-flow" on project-twitch-2k-wager user-stories section (section does not exist yet)
- [x] arch:deployment-twitch-2k-wager label — kustomize overlay component
- [ ] arch note MISSING — [SCOPE] Create architecture note arch-deployment-twitch-2k-wager in pal-e-docs
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#63, open
File Targets
- [x]
overlays/twitch-2k-wager/prod/deployment-patch.yaml— verified: file exists in pal-e-deployments repo, ADMIN_SECRET is NOT present (last env var is STRIPE_WEBHOOK_SECRET at lines 21-25), confirming the bug - [x]
overlays/twitch-2k-wager/dev/deployment.yaml— verified: ADMIN_SECRET correctly mapped at lines 46-50, confirming the pattern to replicate - [x] K8s secret
twitch-2k-wager-secrets— verified via kubectl:admin-secretkey exists in the secret
Targets are specific enough — exact YAML block provided in issue body.
Repo Placement
OK. Issue filed on twitch-2k-wager repo (project tracker) but correctly identifies fix repo as forgejo_admin/pal-e-deployments. Single repo change. Agent must be dispatched against pal-e-deployments, not twitch-2k-wager.
Dependencies
- [x] Board item #817 (twitch-2k-wager#46 — "Fix: ADMIN_SECRET in dev overlay") — satisfied (done column)
- [x] K8s secret admin-secret key — satisfied (confirmed exists in prod namespace)
No unresolved dependencies.
Acceptance Criteria
4 criteria, all verifiable:
- [x] ADMIN_SECRET in prod deployment spec —
kubectl get deployment twitch-2k-wager -n playme2k -o json | jq '.spec.template.spec.containers[0].env' - [x] /admin loads successfully —
curl -s -o /dev/null -w "%{http_code}" https://playme2k.tail5b443a.ts.net/admin - [x] ArgoCD syncs cleanly —
argocd app get playme2k - [x] No regression in existing env vars — diff deployment spec before/after
All criteria are concrete, specific, and agent-testable. No ambiguous language.
Blast Radius
- 1 file touched in 1 repo (pal-e-deployments)
- Change is additive (new env var block), not mutative — existing env vars untouched
- ADMIN_SECRET grep across pal-e-deployments shows only the dev overlay uses it — no other services affected
- Rollback is trivial — revert the single YAML block addition
Decomposition Assessment
Apply the three-thing limit and five-minute rule:
- 1 discrete change (add YAML block to 1 file) — well under 3-thing limit
- Estimated agent time: under 2 minutes — well under 5-minute rule
- No independent subtasks to parallelize — atomic single change
No decomposition needed.
Recommendation
- [SCOPE] Create user-stories section on project-twitch-2k-wager with "operator-flow" story entry — traceability backfill, does not block dispatch
- [SCOPE] Create architecture note arch-deployment-twitch-2k-wager for the kustomize overlay component — traceability backfill, does not block dispatch
Neither recommendation blocks execution. Scope is solid for dispatch.
-
Review: Commit vite.config.ts allowedHosts to repo
review-830-2026-04-05Verdict: READY
Template Completeness
Checked against
template-issue-bug:- [x] Type -- Bug
- [x] Lineage -- "Discovered during validation. Local edit not committed."
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] What Broke -- vite.config.ts missing prod hostname in allowedHosts
- [x] Repro Steps -- present
- [x] Expected Behavior -- present
- [x] Environment -- present (minimal but acceptable for a config-only bug)
- [x] Acceptance Criteria -- present (1 criterion)
- [x] Related -- references #47
Note: Issue includes extra sections from the feature template (File Targets, Test Expectations, Constraints, Checklist). These are harmless additions.
Traceability
- [x] story:challenger-pay label -- challenger payment flow
- [ ] story note NOT VERIFIED -- project-twitch-2k-wager has no TOC entries (project page appears empty or missing user-stories section). [SCOPE] Create user-stories section on project-twitch-2k-wager with challenger-pay story entry.
- [x] arch:twitch-2k-wager-app label -- SvelteKit app component
- [ ] arch note MISSING -- search for "arch-twitch-2k-wager-app" returned no results in pal-e-docs. [SCOPE] Create architecture note arch-twitch-2k-wager-app for the SvelteKit app component.
- [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/forgejo_admin/twitch-2k-wager/issues/59, state: open
File Targets
- [x]
vite.config.ts-- verified exists on main branch. Currently containsallowedHosts: ['playme2k-dev.tail5b443a.ts.net']. Prod hostnameplayme2k.tail5b443a.ts.netis confirmed missing. Issue description is accurate.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager. Fix targets vite.config.ts in the same repo. Single-repo, single-file change.
Dependencies
- [x] #47 (Fix: Stripe webhook delivery for dev) -- satisfied, board item #818 is in done column.
No unresolved dependencies. No in-progress items block this work.
Acceptance Criteria
Single AC: "Both hostnames in allowedHosts." Clear and programmatically verifiable -- agent can grep vite.config.ts for both hostnames after the change. The "Build passes" test expectation is also verifiable. Sufficient for a one-line config bug fix.
Blast Radius
Warning: westside-app has the identical pattern --
vite.config.jscontains only['westside-dev.tail5b443a.ts.net']with no prod hostname. Same bug likely exists there.Other repos checked: mcd-tracker-app and pal-e-playground-archived use
allowedHosts: true(permissive), so they are unaffected.Rollback is trivial -- single config line in a non-breaking direction (adding a hostname to an allowlist).
Decomposition Assessment
No decomposition needed. 1 file target, 1 repo, 1 acceptance criterion, estimated under 2 minutes agent time. Well within the three-thing limit and five-minute rule. No independent subtasks to parallelize.
Recommendation
- [SCOPE] Create user-stories section on project-twitch-2k-wager with challenger-pay story entry.
- [SCOPE] Create architecture note arch-twitch-2k-wager-app for the SvelteKit app component.
- [SCOPE] Create Forgejo issue on forgejo_admin/westside-app for the same allowedHosts bug (only dev hostname, missing prod hostname in vite.config.js).
None of these block the fix itself. The ticket scope is correct and actionable as-is.
-
Validation: PlayMe2K #12
validation-12-2026-04-05Validation: #12 Stripe Connect payout + admin result
Verdict: PASS
Admin result declaration + refund/payout flow live. Woodpecker #33 green. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #11
validation-11-2026-04-05Validation: #11 Stripe Checkout + webhook
Verdict: PASS
Stripe Checkout creates sessions, webhooks deliver. Production Stripe keys active. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #10
validation-10-2026-04-05Validation: #10 Keycloak OIDC auth (Twitch login)
Verdict: PASS
Keycloak OIDC with Twitch IdP functional. Login flow redirects correctly. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #13
validation-13-2026-04-05Validation: #13 Game status UI + email receipts
Verdict: PASS
Game status page and Stripe email receipts working in production. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #47
validation-47-2026-04-05Validation: #47 Fix: Stripe webhook delivery for dev
Verdict: PASS
Webhook delivery fixed for dev environment. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #2
validation-2-2026-04-05Validation: #2 Spike: Stripe Checkout + webhook + payout
Verdict: PASS
Stripe integration live — checkout, webhooks, and payouts functional in production. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #1
validation-1-2026-04-05Validation: #1 Service onboarding: Keycloak + Twitch IdP + CNPG + funnel
Verdict: PASS
Keycloak realm live, Twitch IdP configured, CNPG postgres running, Tailscale funnel active. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #4
validation-4-2026-04-05Validation: #4 Landing page HTML/CSS prototype
Verdict: PASS
Landing page serving at playme2k.tail5b443a.ts.net, curl 200. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #39
validation-39-2026-04-05Validation: #39 Checkout API: guest flow (gamertag + email)
Verdict: PASS
Guest checkout flow live. Stripe Checkout sessions create successfully. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #45
validation-45-2026-04-05Validation: #45 Fix: Stripe redirect http→https
Verdict: PASS
HTTPS redirect working in production. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #54
validation-54-2026-04-05Validation: #54 Replace Connect with refund + payout portal
Verdict: PASS
Payout flow live in production. Woodpecker #33 (Connect fix PR #55) green. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #14
validation-14-2026-04-05Validation: #14 Annotate playground for SvelteKit promotion
Verdict: PASS
Playground annotations applied, SvelteKit app deployed and running. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #57
validation-57-2026-04-05Validation: #57 Observability: blackbox probe + golden signals
Verdict: PASS
pal-e-platform PR #266 merged. Probe + golden signals applied. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #56
validation-56-2026-04-05Validation: #56 Fix: prod pod → playme2k live
Verdict: PASS
Production pod running, curl 200 at playme2k.tail5b443a.ts.net. See
validation-playme2k-launch-2026-04-05for full evidence. -
Validation: PlayMe2K #46
validation-46-2026-04-05Validation: #46 Fix: ADMIN_SECRET in dev overlay
Verdict: PASS
Dev overlay pod running (0 restarts). See
validation-playme2k-launch-2026-04-05for full evidence. -
Review: Observability: blackbox probe + golden signals
review-826-2026-04-03Verdict: READY
Template Completeness
- [x] Type — Feature
- [x] Lineage — "Required for production. Marcus needs uptime assurance during streams."
- [x] Repo — forgejo_admin/pal-e-platform
- [x] User Story — clear operator story with motivation
- [x] Context — explains monitoring pattern and why it matters for playme2k
- [x] File Targets — 2 specific paths with descriptions
- [x] Acceptance Criteria — 4 testable conditions
- [x] Test Expectations — tofu plan + Prometheus verification
- [x] Constraints — pattern matching, internal URL, lock flag
- [x] Checklist — PR, tests, no unrelated changes
- [x] Related — links to story note, arch note, project page
Traceability
- [x] story:observability — Platform Observability. Story note story-twitch-2k-wager-observability (id 1166) exists. Key "observability" listed in project-twitch-2k-wager user-stories table.
- [x] arch:deployment-twitch-2k-wager — Deployment topology. Arch note arch-deployment-twitch-2k-wager (id 1156) exists in pal-e-docs.
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#57, open
File Targets
- [x]
terraform/modules/monitoring/main.tf— verified exists. Blackbox exporter targets array at line 342. Dashboard ConfigMap pattern at line 644. Agent adds a new target entry and a new ConfigMap resource. - [x]
terraform/dashboards/— verified directory exists.pal-e-docs-golden-signals.jsonavailable as copy template.playme2k-golden-signals.jsondoes not yet exist (expected — ticket creates it).
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager (service repo) for traceability.
### Repofield correctly identifies forgejo_admin/pal-e-platform as the target repo for code changes. Single-repo change — no cross-repo coordination needed.Dependencies
- [x] pal-e-platform repo access — satisfied (working directory)
- [x] Existing monitoring patterns — satisfied (blackbox exporter + dashboard ConfigMaps already deployed)
- [ ] playme2k service running — pending (board item #825, issue #56 "Fix: prod pod" is in backlog). However, Terraform resources can be applied independently. Probe will report "down" until service is live, matching existing patterns. Not a blocking dependency for implementation.
Acceptance Criteria
4 AC, all testable by an agent:
- "Blackbox probe configured" — verifiable via
tofu plan -lock=falseoutput showing new target entry - "Probe appears in Prometheus targets" — verifiable post-apply via Prometheus UI (post-merge validation, not agent-gated)
- "Grafana dashboard shows golden signals" — verifiable via ConfigMap creation in plan output
- "Alerts fire on downtime" — covered automatically by existing
EndpointDownPrometheusRule (line 445,probe_success == 0) which fires for all blackbox targets. No new alert rule needed.
Test expectations are realistic:
tofu plan -lock=falsefor pre-merge, Prometheus target check for post-apply.Blast Radius
- 2 files touched in 1 repo (monitoring/main.tf + new dashboard JSON)
- No changes to existing probes or dashboards
- Existing EndpointDown alert automatically covers the new target
- No downstream consumers affected
- Rollback: remove target entry + ConfigMap + delete dashboard JSON. Straightforward.
Decomposition Assessment
- 2 file targets in 1 repo — under the 3-file limit
- 4 acceptance criteria — under the 5 AC limit
- Estimated agent time: 2-3 minutes (copy dashboard template + add probe target + add ConfigMap) — under the 5-minute rule
- No independent subtasks that need parallelization
- No decomposition needed.
Recommendation
- No action needed. Scope is solid, all file targets verified, traceability complete, fits in a single agent pass.
Implementation note: The probe URL in the Constraints section (
http://twitch-2k-wager-app.playme2k.svc.cluster.local:3000/) uses namespaceplayme2kwhile the arch diagram saystwitch-2k-wager namespace. The agent should verify the actual k8s namespace at implementation time by checking the pal-e-deployments kustomize overlay. This is an implementation detail, not a scoping issue. -
Review: Replace Connect with refund + payout portal
review-824-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — Replaces Connect-based payout
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — clear who/want/so-that
- [x] Context — explains Connect UX problem and new approach
- [x] File Targets — 12 specific files listed with descriptions
- [x] Acceptance Criteria — 8 testable conditions
- [x] Test Expectations — 6 test items with CI pass requirement
- [x] Constraints — 4 constraints documented
- [x] Checklist — PR, tests, no unrelated changes
- [x] Related — links to story notes, arch notes, domain model
Traceability
- [x] story:winner-payout label — Winner Payout and Receipt (story note id 1160, verified on project-twitch-2k-wager user-stories table)
- [x] story:operator-flow label — Marcus Operator Flow (story note id 1210 exists)
- [ ] story:operator-flow MISSING from project page — [SCOPE] Add operator-flow row to project-twitch-2k-wager user-stories table
- [x] arch:dataflow-twitch-2k-wager label — Data Flow: Twitch 2K Wager (arch note id 1155 exists)
- [ ] arch note STALE — [SCOPE] arch-dataflow-twitch-2k-wager diagram still shows Stripe Connect Express flow (createConnectAccount, getOnboardingLink, createTransfer, account.updated webhook). Must be updated to reflect refund + payout portal flow.
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#54, state: open
File Targets
Existing files to modify (all verified present on main):
- [x]
src/lib/stripe.ts— verified: createConnectAccount (L107), getOnboardingLink (L125), createTransfer (L143) all present for removal. createRefund to be added. - [x]
src/routes/api/webhooks/stripe/+server.ts— verified: account.updated handler present via handleAccountUpdated function. - [x]
src/routes/api/admin/result/+server.ts— verified: imports createConnectAccount, getOnboardingLink, createTransfer, updateConnectStatus. Connect logic confirmed. - [x]
src/lib/schema.sql— verified: payout table exists with stripe_transfer_id, status CHECK includes 'onboarding'. Needs payout_token, payout_method, payout_handle columns. - [x]
src/lib/db.ts— verified: exists with Payout type import. New functions needed. - [x]
src/lib/types.ts— verified: Payout interface has stripe_transfer_id; PayoutStatus includes 'onboarding'. Challenger has stripe_connect_account_id and connect_onboarding_status. - [x]
src/routes/admin/+page.svelte— verified: exists with game queue UI. Needs "Mark Paid" in completed section.
New files (parent directories verified or to be created):
- [x]
src/routes/payout/[token]/+page.svelte— NEW, parent dir exists (src/routes/payout/) - [x]
src/routes/payout/[token]/+page.server.ts— NEW - [x]
src/routes/api/payout/[token]/+server.ts— NEW - [x]
src/routes/api/admin/mark-paid/+server.ts— NEW
Missing from file targets:
- [ ]
src/routes/payout/+page.svelte— ISSUE: old Connect onboarding status page exists but not mentioned for deletion. Will become dead code or conflict with new [token] route. - [ ]
src/lib/__tests__/stripe.test.ts— ISSUE: existing tests likely cover Connect functions. Not listed for update. - [ ]
src/lib/email.ts— ISSUE: AC mentions winner/loser emails but email.ts not in file targets. Unclear if templates change. - [ ] challenger table columns — ISSUE: stripe_connect_account_id and connect_onboarding_status become dead columns after Connect removal. Not mentioned for cleanup.
Repo Placement
OK. All work is in forgejo_admin/twitch-2k-wager. No cross-repo concerns. Schema changes, API endpoints, and UI are all in the same SvelteKit app.
Dependencies
- [x] DB schema (issue #9, board #795) — done. Schema exists, payout table present.
- [x] Stripe Checkout + webhook (issue #11, board #797) — validation. Payment flow working.
- [ ] Board #798 (issue #12, "Stripe Connect payout + admin result") — in validation column. This is the OLD Connect implementation that #54 replaces. Should be marked superseded before this ticket proceeds.
- [ ] Board #814 (issue #40, "Admin queue view for Marcus") — in needs_approval. Also touches admin page + operator-flow story. "Mark Paid" button in #54 overlaps with admin queue scope. Needs coordination.
- [ ] Board #793 (issue #7, "SvelteKit app" umbrella) — in backlog. Parent ticket also carries story:winner-payout. #54 carves payout rework out of #7.
Acceptance Criteria
8 AC total. Assessment:
- [x] "Challenger wins -> Stripe refund fires" — testable via Stripe mock.
- [x] "Payout record created with unique token" — testable via DB query.
- [x] "/payout/[token] page shows form" — testable via page render + DOM check.
- [x] "Submitting payout form saves method + handle" — testable via POST + DB verify.
- [x] "Admin queue shows pending payouts with handle" — testable via admin page render.
- [x] "Mark Paid button marks payout as sent" — testable via button click + DB verify.
- [x] "All Connect code removed" — testable via grep for removed function names.
- [x] "Marcus wins flow unchanged" — testable by verifying existing revenue split logic still works.
All AC are testable. Two email-related AC ("winner gets email", "loser gets email") appear in the user story note but are not in this issue's AC — confirm whether email changes are in or out of scope.
Blast Radius
- challenger table — stripe_connect_account_id and connect_onboarding_status columns become dead after Connect removal. Should be cleaned up to avoid confusion.
- Existing tests — src/lib/__tests__/stripe.test.ts likely tests createConnectAccount, getOnboardingLink, createTransfer. Will break after removal.
- Old payout page — src/routes/payout/+page.svelte references Connect onboarding UX. Dead page after this change.
- Webhook handler — removing account.updated is safe since no other flow depends on it. checkout.session.completed stays.
- Rollback — schema migration (adding columns) is forward-only but non-destructive. Connect removal is a code change, rollback via git revert.
Decomposition Assessment
NEEDS DECOMPOSITION.
- File count: 12 file targets (7 modify + 4 new + 1 delete) — exceeds 3-thing limit.
- AC count: 8 acceptance criteria — exceeds 5 AC threshold.
- Test count: 6 test expectations.
- Estimated time: Well over 5 minutes — touches DB schema, Stripe API integration, 3 new routes, 2 existing endpoint refactors, admin UI, and test updates.
- Independent subtasks exist: Yes. Connect removal, payout portal, admin result refactor, and mark-paid endpoint are largely independent.
Recommended split into 3-4 sub-tickets via skill-decompose-ticket:
- Remove Connect + add refund — stripe.ts cleanup, webhook cleanup, schema migration, types.ts, db.ts new functions, challenger table cleanup
- Payout portal UI + API — new payout/[token] routes (3 files), delete old payout page
- Admin result endpoint refactor — update result/+server.ts to fire refund + create payout token
- Admin Mark Paid — new mark-paid endpoint + admin page UI update
Recommendation
[SCOPE]Add operator-flow row to project-twitch-2k-wager user-stories table. Story note exists (id 1210) but is not listed on the project page.[SCOPE]Update arch-dataflow-twitch-2k-wager diagram to reflect refund + payout portal flow. Current diagram shows stale Connect Express flow.[BODY]Addsrc/routes/payout/+page.svelteto file targets as "DELETE — old Connect onboarding status page."[BODY]Addsrc/lib/__tests__/stripe.test.tsto file targets — existing Connect tests need removal/update.[BODY]Clarify email scope: addsrc/lib/email.tsto file targets if email content changes, or note that existing email logic is reused unchanged.[BODY]Add note about removing stripe_connect_account_id and connect_onboarding_status from challenger table schema + Challenger type.[DECOMPOSE]12 file targets, 8 AC across DB/API/UI/Stripe layers. Route to skill-decompose-ticket for sub-board creation with 3-4 sub-tickets.
-
Review: Checkout API: guest flow (gamertag + email)
review-813-2026-04-03-r3Verdict: NEEDS_REFINEMENT
Third review pass (board item #813). Prior fixes (4 downstream file targets + 6 test files + PK migration strategy) confirmed present. Two additional files discovered that are impacted by the guest flow but missing from file targets.
Template Completeness
- [x] Type — Feature
- [x] Lineage — Required by #38 and #40
- [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 and migration plan
- [x] File Targets — 9 source files + 6 test files listed
- [x] Acceptance Criteria — 10 testable criteria
- [x] Test Expectations — 4 expectations with CI gate
- [x] Constraints — backward compat, dedup keys, idempotent migration
- [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
Source files (9 listed — 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) - [x]
src/lib/types.ts— verified: Challenger interface has twitch_id: string (line 28), no numeric id field - [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) - [x]
src/routes/api/checkout/+server.ts— verified: requires twitch_id in body (line 38), calls createGame(challenger.twitch_id) (line 59) - [x]
src/lib/stripe.ts— verified: createCheckoutSession metadata uses challenger_twitch_id string key (line 84) - [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) - [x]
src/routes/game/[id]/+page.server.ts— verified: ownership check session.twitchId !== game.challenger_id (line 26), getChallenger(game.challenger_id) (line 32) - [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) - [x]
src/lib/auth.ts— verified: SessionData.twitchId is required string (line 181), used throughout session encode/decode
Test files (6 listed — 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
Missing file targets (2):
- [ ]
src/hooks.server.ts— ISSUE: Line 13 definesPROTECTED_PREFIXES = ['/game']and line 41 redirects unauthenticated users to /auth/login. Guest checkout creates games visible at /game/[id], but guests have no session. AC #8 says "Game status page handles guest games" but the hooks auth gate prevents guests from ever reaching the page.server.ts logic. This file must be updated to allow unauthenticated /game/[id] access. - [ ]
src/app.d.ts— MINOR: Defines App.Locals.session as SessionData | null. If auth.ts makes twitchId optional per the auth.ts file target, the type flows through app.d.ts correctly since session is already nullable. Low risk, but should be listed for completeness since the agent needs to verify type coherence.
Repo Placement
OK. All file targets are in forgejo_admin/twitch-2k-wager. Single-repo change. Auth callback (
src/routes/auth/callback/+server.ts) also references twitch_id but is backward-compatible — it calls the same upsertChallenger function whose signature is preserved.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. Schema and webhook handler exist. Satisfied.
- [x] #9 (Postgres schema + db.ts) — prerequisite, done. Schema exists at src/lib/schema.sql. Satisfied.
- [x] No unresolved upstream blockers.
Acceptance Criteria
10 AC are well-defined and testable by an agent. Assessment:
- AC 1-6: Straightforward 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 handles guest games" — BLOCKED by hooks.server.ts auth gate. The page.server.ts ownership logic is correctly scoped, but guests are redirected before reaching it. Requires hooks.server.ts update to be verifiable.
- AC 9: Webhook handler uses INT challenger_id — testable via webhook.test.ts.
- AC 10: All 6 test files updated and passing — testable via
npm test.
4 test expectations are clear and CI-verifiable.
Blast Radius
twitch_idappears in 16 files / 46 occurrences across src/.- 15 of 16 files are covered by file targets. The 16th (auth callback) is backward-compatible.
- hooks.server.ts is the only functional gap — it gates /game/* access and will break guest game viewing.
- Rollback: schema migration adds columns and changes PK. Needs a reverse migration. Not trivial but standard.
- No other repos affected. No downstream services consume this API yet (#38 and #40 are not implemented).
Decomposition Assessment
Apply the three-thing limit and five-minute rule:
- 11 source files (9 listed + 2 missing) + 6 test files = 17 files in one repo.
- 10 AC + 4 test expectations = 14 verification points.
- All changes are in one repo and driven by a single schema migration — tightly coupled, not independently parallelizable.
- The migration must happen first, then types, then all consumers update together. Sequential dependency chain.
- Estimated agent time: 5-8 minutes. Borderline but acceptable as a single pass because the changes are mechanical (find twitch_id references, update to INT id) after the schema/type changes.
- No decomposition needed. Single agent pass is appropriate once file targets are corrected.
Recommendation
[BODY]Addsrc/hooks.server.tsto File Targets under "Downstream consumers" with note: "Update PROTECTED_PREFIXES or add guest-aware logic so unauthenticated users can view /game/[id] for guest games."[BODY]Addsrc/app.d.tsto File Targets under "Downstream consumers" with note: "Verify type coherence after SessionData.twitchId becomes optional."[BODY]Clarify AC #8: "Game status page handles guest games" should explicitly mention that the hooks.server.ts auth redirect must be relaxed for guest game pages, not just the page.server.ts ownership check.
-
Review: Checkout API: guest flow (gamertag + email)
review-813-2026-04-03Verdict: NEEDS_REFINEMENT
Re-review (2026-04-03). Previous review flagged PK migration strategy missing. Issue body now specifies: serial id as new PK, nullable twitch_id + UNIQUE, gamertag column, FK update, all db.ts function updates. PK strategy is resolved. Remaining issues: missing file targets and acceptance criteria for downstream consumers.
Template Completeness
- [x] Type — Feature
- [x] Lineage — "Required by the /play guest checkout flow (#38)"
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — clear challenger-focused story
- [x] Context — explains current twitch_id PK design, guest need, and full migration strategy (6-step plan)
- [x] File Targets — 5 files + generic "Tests" entry
- [x] Acceptance Criteria — 7 testable criteria
- [x] Test Expectations — unit tests for guest + auth flows, CI
- [x] Constraints — backward compatibility, email dedup for guests, twitch_id dedup for auth, idempotent migration
- [x] Checklist — standard PR/test/no-unrelated
- [x] Related — links to #38, story notes, arch notes
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 id 1158)
- [x] arch:dataflow-twitch-2k-wager label — Data Flow: Twitch 2K Wager
- [x] arch note verified — arch-dataflow-twitch-2k-wager (id 1155) exists in pal-e-docs
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#39, open
File Targets
Previously flagged (now resolved):
- [x]
src/lib/schema.sql— verified: twitch_id is TEXT PRIMARY KEY (line 7), game.challenger_id is TEXT REFERENCES challenger(twitch_id) (line 21). Issue now specifies full 6-step migration plan. Accurate. - [x]
src/routes/api/checkout/+server.ts— verified: currently requires twitch_id, twitch_username, email (lines 30-38). Guest path needs gamertag + email without twitch_id. Correct target. - [x]
src/lib/db.ts— verified: getChallenger queries by twitch_id (line 54), upsertChallenger uses ON CONFLICT(twitch_id) (line 67), createGame takes challengerId as string (line 96), updateConnectStatus uses twitch_id (line 85), listGamesByChallenger queries by challenger_id (line 133). Issue correctly calls for createGuestChallenger + PK updates. - [x]
src/lib/types.ts— verified: Challenger.twitch_id is string (line 28), Game.challenger_id is string (line 39). Issue correctly specifies: id (number), twitch_id (optional), gamertag (string). - [x]
src/routes/api/webhooks/stripe/+server.ts— verified: queries challenger by twitch_id (line 142-143), joins game.challenger_id as TEXT (line 176). Needs update for new INT PK.
Still missing from issue body:
- [ ]
src/routes/api/admin/result/+server.ts— MISSING. Calls getChallenger(game.challenger_id) on line 101. Uses challenger.twitch_id on lines 131, 153, 169 for Connect status updates, transfer initiation, and logging. Will break when game.challenger_id changes from TEXT to INT and when getChallenger signature changes. - [ ]
src/routes/game/[id]/+page.server.ts— MISSING. Calls getChallenger(game.challenger_id) on line 32. Ownership check on line 26 compares session.twitchId !== game.challenger_id — this comparison breaks entirely when challenger_id becomes INT. Guest games also have no session.twitchId. - [ ]
src/lib/stripe.ts— MISSING. Stores challenger_twitch_id in Stripe Checkout metadata (line 84). Guest challengers have no twitch_id — metadata key needs conditional handling (gamertag or challenger serial id). - [ ]
src/lib/auth.ts— MISSING (minor). References twitch_id in session type (line 142). Game status page ownership check depends on this. - [ ] Test files — issue says "Tests — update existing tests" generically. Should enumerate the 6 affected files:
checkout.test.ts,webhook.test.ts,result.test.ts,page-server.test.ts,db.test.ts,stripe.test.ts. All hardcode twitch_id/challenger_id as string patterns.
Repo Placement
OK. All files in forgejo_admin/twitch-2k-wager. Single repo change. No cross-repo work.
Dependencies
- [x] Board item #812 (/play guest checkout route, issue #38) — consumer of this endpoint. In backlog. This ticket is the upstream dependency. No blocker.
- [x] Board item #797 (Stripe Checkout + webhook, issue #11) — in validation. The existing auth checkout flow this ticket must remain backward-compatible with. Confirms backward-compat constraint is real.
- [x] Board item #796 (Keycloak OIDC auth, issue #10) — in validation. Provides session.twitchId used in game/[id] ownership check. That check must be updated for both auth paths.
- [x] Board item #795 (Postgres schema + db.ts, issue #9) — done. The original schema this ticket migrates. Satisfied.
- No unresolved blockers.
Acceptance Criteria
7 AC present, all testable by an agent. 3 missing criteria for downstream consumers not listed in file targets:
- Missing: "Admin result endpoint works with new challenger PK — getChallenger retrieves by serial id, Connect status updates use correct identifiers"
- Missing: "Game status page ownership check works for both Twitch-auth and guest challengers — session.twitchId comparison updated for new PK model"
- Missing: "Stripe Checkout metadata includes gamertag for guest flow — challenger_twitch_id is nullable or supplemented with gamertag"
Blast Radius
Significant but contained to one repo. The PK migration from twitch_id (TEXT) to serial id (INT) touches every consumer of the challenger table:
- admin/result endpoint — loads challenger by game.challenger_id, uses challenger.twitch_id for Connect status updates and logging. Both the lookup pattern and the twitch_id references need updating.
- game/[id] page — ownership check compares session.twitchId to game.challenger_id. After migration challenger_id is INT. Guest games have no session.twitchId at all — need alternative ownership mechanism.
- Stripe metadata — stores challenger_twitch_id. Guest flow needs gamertag or serial id as identifier in metadata.
- Webhook handler — processPendingPayouts joins game.challenger_id (changes type from TEXT to INT reference).
- Architecture notes — arch-domain-twitch-2k-wager ER diagram shows twitch_id as PK. arch-dataflow-twitch-2k-wager shows Twitch-only identity flow. Both need post-implementation updates (separate tickets).
- Rollback is straightforward (revert migration + code). No sibling services affected.
Decomposition Assessment
9+ file targets (including 4 missing) in 1 repo. 7 AC present (should be 10). All changes are tightly coupled — schema migration drives type changes drives db function changes drives endpoint changes. Splitting would create merge conflicts and require coordinated migration state. Applying the three-thing limit: 3 conceptual changes (schema migration, guest checkout path, consumer updates). Applying the five-minute rule: borderline at 4-6 minutes with a complete spec, but the tight coupling justifies a single agent pass. No decomposition needed if file targets and AC are completed — the work is cohesive.
Recommendation
[BODY]Add missing file target:src/routes/api/admin/result/+server.ts— update getChallenger call (line 101) and challenger.twitch_id references (lines 131, 153, 169) for new INT PK model[BODY]Add missing file target:src/routes/game/[id]/+page.server.ts— update ownership check (line 26: session.twitchId !== game.challenger_id) and getChallenger call (line 32) for new PK. Add guest ownership mechanism.[BODY]Add missing file target:src/lib/stripe.ts— update Stripe Checkout metadata (line 84) to handle guest flow (gamertag or serial id when twitch_id unavailable)[BODY]Add missing file target:src/lib/auth.ts— session type twitch_id reference (line 142), needed for updated ownership check[BODY]Enumerate test files explicitly:checkout.test.ts,webhook.test.ts,result.test.ts,page-server.test.ts,db.test.ts,stripe.test.ts[BODY]Add AC: "Admin result endpoint works with new challenger PK"[BODY]Add AC: "Game status page ownership check works for both Twitch-auth and guest challengers"[BODY]Add AC: "Stripe Checkout metadata includes gamertag for guest flow"
-
Review: Admin queue view for Marcus (re-review)
review-814-2026-04-03-v2Verdict: READY
Re-review of board item #814 after body + label fixes from first review. All three prior issues resolved.
Template Completeness
- [x] Type — Feature
- [x] Lineage — depends on #39, standalone otherwise
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — operator-flow: Marcus sees paid challengers + gamertags
- [x] Context — admin queue, ADMIN_SECRET auth, depends on #39 for gamertag field
- [x] File Targets — 4 files listed
- [x] Acceptance Criteria — 8 criteria
- [x] Test Expectations — 3 test items
- [x] Constraints — auth pattern, gamertag dependency, dark theme, minimal
- [x] Checklist — standard PR checklist
- [x] Related — #39, #12, story note, arch note
Traceability
- [x] story:operator-flow label — Marcus Operator Flow
- [x] story note verified — story-twitch-2k-wager-operator-flow (id 1210) exists, active, note_type=user-story
- [ ] story NOT on project page — [SCOPE] Add operator-flow row to project-twitch-2k-wager user-stories table
- [x] arch:dataflow-twitch-2k-wager label — Data Flow: Twitch 2K Wager
- [x] arch note verified — arch-dataflow-twitch-2k-wager (id 1155) exists, active, note_type=architecture
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#40, state=open
File Targets
- [x]
src/routes/admin/+page.svelte— new file (admin route does not exist yet, parent dir src/routes/ exists, expected for feature) - [x]
src/routes/admin/+page.server.ts— new file (expected) - [x]
src/routes/admin/+layout.server.ts— new file (admin auth guard, expected) - [x]
src/lib/db.ts— verified exists. Currently has listQueuedGames() but no join query. Ticket correctly identifies need for listGamesWithChallengers() join query. Fix from prior review confirmed present in issue body.
Targets are specific enough for an agent to act on without guessing.
Repo Placement
OK. All 4 file targets are in forgejo_admin/twitch-2k-wager. Issue is filed on the same repo. No cross-repo work.
Dependencies
- [x] #39 (guest checkout API — gamertag field) — status: pending (open, board item #813 in backlog). Adds gamertag column to challenger table. This ticket's join query needs that column. Dependency is explicitly documented in Lineage + Context + Constraints. Must be merged before this ticket can be implemented.
- [x] #12 (admin result endpoint) — status: satisfied (board item #798 in validation, code exists at src/routes/api/admin/result/+server.ts). Uses Bearer ADMIN_SECRET auth pattern. This ticket's result buttons will call that existing endpoint. No changes needed.
Acceptance Criteria
8 criteria. All are testable by an agent:
- AC 1: /admin?key=ADMIN_SECRET shows queue + sets cookie — testable via server load function + cookie check
- AC 2: each game shows gamertag, email, tier, pot, status, timestamp — testable via DOM check with test data
- AC 3: "Challenger Wins" and "Marcus Wins" buttons on active games — testable via component render
- AC 4: result button calls /api/admin/result with Bearer header — testable via fetch mock or integration test
- AC 5: completed games show result + payout status — testable via DOM check
- AC 6: auto-refresh poll every 5s — testable via setInterval presence in component
- AC 7: mobile-friendly — testable via responsive CSS check
- AC 8: returns 401 without valid secret — testable via server load function test
Auth pattern (query param to cookie to Bearer for API) is clarified in Constraints. Matches existing admin result endpoint's Bearer pattern. No ambiguity.
Blast Radius
Low. New route (/admin) with no existing consumers. The only shared code touched is db.ts (adding a new exported function, not modifying existing ones). The admin result endpoint (#12) is called from the new page but not modified. No downstream consumers affected. Rollback is straightforward — delete the admin route directory and the one db function.
Decomposition Assessment
4 file targets in 1 repo. 8 AC (above the 5-rule threshold). However, all targets are tightly coupled — 3 files form a single SvelteKit admin route, plus 1 db query addition. No independent subtasks that would benefit from parallelization. Estimated agent time: 3-4 minutes for a single coherent pass. No decomposition needed.
Prior Review Issues (resolved)
- story label mismatch — fixed. Label is now story:operator-flow. Story note story-twitch-2k-wager-operator-flow created (id 1210).
- missing db.ts query — fixed. Issue body now specifies listGamesWithChallengers() join query in file targets.
- gamertag gap — resolved. Documented as dependency on #39. Schema has no gamertag column; #39 adds it.
- auth pattern — clarified. Query param to cookie to Bearer header for API. Consistent with existing admin result endpoint.
Recommendation
[SCOPE]Add operator-flow row to project-twitch-2k-wager user-stories table (story note exists but project page table is missing the entry). Not a blocker for implementation.
-
Review: Admin queue view for Marcus
review-814-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — Standalone
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — As Marcus (operator), wants to see paid challengers + gamertags
- [x] Context — adequate operator motivation, ADMIN_SECRET auth decision stated
- [x] File Targets — 3 new files specified
- [x] Acceptance Criteria — 7 items
- [x] Test Expectations — 3 items
- [x] Constraints — present (auth pattern, dark theme, minimal)
- [x] Checklist — present
- [x] Related — references #12, story note, arch note
Traceability
- [x] story:game-status label — present on board item
- [x] story note exists — story-twitch-2k-wager-game-status (id 1159) found in pal-e-docs
- [ ] story mismatch — [SCOPE] The story
story-twitch-2k-wager-game-statusis written from the Challenger perspective ("As a challenger, I want to see on the website that my payment was received"). This ticket is from the Operator (Marcus) perspective ("As Marcus, I want to see a list of people who have paid"). These are different user roles with different needs. Recommend creatingstory-twitch-2k-wager-operator-queueand re-labeling the board itemstory:operator-queue. - [x] arch:dataflow-twitch-2k-wager label — present on board item
- [x] arch note verified — arch-dataflow-twitch-2k-wager (id 1155) exists. Diagram includes "Operator declares winner" flow which directly aligns with this ticket.
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#40, open
File Targets
- [x]
src/routes/admin/+page.svelte— NEW file. Parentsrc/routes/exists. No existingadmin/directory. Correct placement for SvelteKit page route. - [x]
src/routes/admin/+page.server.ts— NEW file. Server load function to fetch games from DB. Correct SvelteKit pattern. - [x]
src/routes/admin/+layout.server.ts— NEW file. Auth guard for admin routes. Correct SvelteKit pattern. - [ ] MISSING target — [BODY]
src/lib/db.tsmust be added to File Targets. The admin page needs a new query function (e.g.listAllGamesWithChallengers()) that joins thegameandchallengertables. Current DB functions are insufficient:listQueuedGames()returns only queued games with no challenger join;listGamesByChallenger()filters to a single challenger. The admin page needs all games (across all statuses) with challenger info.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, all file targets are in this repo. Single-repo change. No cross-repo concerns.
Dependencies
- [x] #12 (Stripe Connect payout + admin result) —
validationcolumn, Forgejo issue closed. The admin page calls/api/admin/resultwhich exists atsrc/routes/api/admin/result/+server.ts. Satisfied. - [x] #9 (Postgres schema + db.ts) —
donecolumn. DB layer and types exist. Satisfied. - [x] #13 (Game status UI + email receipts) —
validationcolumn. Challenger-facing, independent from admin view. No dependency.
No unresolved dependencies that block execution.
Acceptance Criteria
- [x] "
/admin?key=ADMIN_SECRETshows the queue" — testable, clear - [ ] "Each game shows: gamertag, email, tier amount, pot amount, status, timestamp" — [BODY] gamertag data gap. The
Challengertype (src/lib/types.ts) hastwitch_usernameandemailbut nogamertagfield. The frontend (+page.svelte,play/+page.svelte) collects gamertag input but the checkout API (/api/checkout/+server.ts) callsupsertChallenger(twitchId, twitchUsername, email)— gamertag is never persisted. Either: (a) clarify thattwitch_usernameserves as the display identifier (update AC wording), or (b) add a gamertag column (separate prerequisite ticket touching schema + checkout API). - [x] "Challenger Wins / Marcus Wins buttons on each active game" — testable, clear
- [x] "Clicking a result button calls /api/admin/result" — testable, existing endpoint
- [x] "Completed games show result + payout status" — testable
- [x] "Auto-refreshes (poll every 5s)" — testable
- [x] "Mobile-friendly" — testable visually
Blast Radius
- Auth pattern divergence: The existing admin result endpoint (
src/routes/api/admin/result/+server.ts) authenticates viaAuthorization: Bearer ADMIN_SECRETheader. This ticket proposes query-param auth (?key=ADMIN_SECRET) for the admin page. Two different auth mechanisms for the same admin surface. The agent needs guidance on how the page JS should authenticate calls to/api/admin/result— either include a Bearer header from client-side JS, or have the layout guard set a cookie that the API also accepts. [BODY] Add a note in Constraints clarifying how admin page authenticates API calls. - No external blast radius. Admin page is internal operator tooling. No downstream consumers affected.
- Rollback is straightforward — delete the 3 new files and the DB function.
Decomposition Assessment
3 new file targets + 1 DB function modification, all in 1 repo. 7 AC items but they describe a single cohesive page. Estimated agent time: 3-4 minutes for a standard SvelteKit CRUD page with server load + auth guard + polling.
No decomposition needed — fits the three-thing limit (3 new routes + 1 db function = one coherent feature) and five-minute rule. No independent subtasks worth parallelizing.
Prerequisite: the gamertag data gap must be resolved before dispatching. If it requires a schema migration, that becomes a separate prerequisite ticket.
Recommendation
- [SCOPE] Create user story
story-twitch-2k-wager-operator-queue("As an operator, I want to see paid challengers and their gamertags so I can invite them and declare results"). Add to project-twitch-2k-wager user-stories table. Update board item #814 label fromstory:game-statustostory:operator-queue. - [BODY] Add
src/lib/db.tsto File Targets: "AddlistAllGamesWithChallengers()function joining game + challenger tables for the admin view." - [BODY] Resolve gamertag data gap: clarify in AC whether
twitch_usernameis the display identifier (rename "gamertag" to "twitch username" in AC), or scope a prerequisite ticket to add a gamertag column to the challenger table and update the checkout flow. - [BODY] Add to Constraints: "Admin page JS must include
Authorization: Bearer ADMIN_SECRETheader when calling/api/admin/result, matching the existing endpoint's auth pattern. The layout guard validates the query param on initial page load and stores the secret client-side for subsequent API calls."
-
Review: /play guest checkout route + landing updates
review-812-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — "New primary flow — direct challenge link. Standalone."
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — clear challenger wants frictionless guest checkout
- [x] Context — explains /play route purpose, landing page CTA change, code exists locally
- [x] File Targets — 6 files, all specific with change descriptions
- [x] Acceptance Criteria — 6 testable conditions
- [x] Test Expectations — 3 items including CI pipeline
- [x] Constraints — code exists locally, no new dependencies
- [x] Checklist — standard PR/tests/no-unrelated
- [x] Related — project page, story note, arch note referenced
Traceability
- [x] story:challenger-pay label — Secure Buy-In Payment
- [x] story note verified — challenger-pay listed in project-twitch-2k-wager user-stories table (key: challenger-pay, note: story-twitch-2k-wager-challenger-pay)
- [x] arch:dataflow-twitch-2k-wager label — Data Flow: Twitch 2K Wager
- [x] arch note verified — arch-dataflow-twitch-2k-wager (id 1155) exists in pal-e-docs
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#38, open
File Targets
- [x]
src/routes/play/+page.svelte— verified: NEW file exists with tier pills, gamertag + email form, handleCheckout calling /api/checkout - [x]
src/routes/+page.svelte— verified: MODIFIED with clickable tier cards, $1 test tier (dashed red border), gamertag input, checkout form replacing Twitch-login-first CTA - [x]
src/app.css— verified: MODIFIED with .tier-card, .tier-card.selected, .checkout-form styles, tier color vars - [x]
src/lib/stripe.ts— verified: MODIFIED with $1 test tier (amount: 100, label: "Test Challenge") in TIERS map, VALID_TIERS = [1, 15, 30, 45] - [x]
src/lib/types.ts— verified: MODIFIED with 100 added to BuyInAmount union (100 | 1500 | 3000 | 4500) - [x]
src/lib/schema.sql— verified: MODIFIED with 100 added to buy_in_amount CHECK constraint
Repo Placement
Correct. Issue filed on forgejo_admin/twitch-2k-wager, all 6 file targets in the same repo. No cross-repo scope.
Dependencies
- [ ] Board item #813 / Forgejo issue #39 ("Checkout API: guest flow — gamertag + email") — HARD DEPENDENCY, UNDOCUMENTED. The current
/api/checkout/+server.ts(line 30-38) destructures{ tier, twitch_id, twitch_username, email }and returns 400 iftwitch_idortwitch_usernameis missing. The /play guest page sends{ tier, gamertag, email }with no twitch_id. Without #39 merged first, every guest checkout returns 400. Both items currently in backlog.
Acceptance Criteria
6 AC, all individually testable. AC #1-2 and #4-6 are purely frontend and verifiable by rendering. AC #3 ("Pay $X & Play button calls /api/checkout") will fail with a 400 until the backend dependency (#39) is resolved. An implementing agent cannot fully validate AC #3 without either mocking the endpoint or having #39 merged first.
Blast Radius
- The $1 test tier addition is consistent across all three type-system files (stripe.ts: TIERS + VALID_TIERS, types.ts: BuyInAmount union, schema.sql: CHECK constraint). No mismatch detected.
- The schema.sql CHECK constraint change (adding 100) requires a migration or ALTER TABLE on the live database. The issue does not mention a migration strategy. This is a deployment concern, not a code concern.
- Landing page (/) CTA changes from Twitch login to gamertag entry. Twitch login preserved as optional path. The existing authenticated checkout flow (Keycloak OIDC) is unaffected — the same /api/checkout endpoint is called with the same payload shape from the auth path.
- Rollback is straightforward — revert the branch. No database schema applied yet.
Decomposition Assessment
6 file targets in 1 repo, 6 AC. Borderline on the three-thing limit. However, the issue explicitly states code is already written locally on archbox — agent work is commit, push, and CI verification, not implementation. Estimated agent time well under 5 minutes. No independent subtasks to parallelize. No decomposition needed.
Recommendation
[BODY]Add a Dependencies section to the issue body documenting the hard dependency on #39 (Checkout API guest flow). The /play frontend sends{ tier, gamertag, email }but the current backend requires{ tier, twitch_id, twitch_username, email }. Execution order: #39 before #812, or merge both in a single branch.[BODY]Add a note under Constraints about schema migration strategy for the $1 test tier — the CHECK constraint change (adding 100 to buy_in_amount) needs an ALTER TABLE or migration on the live database.
-
Validation: Dev overlay (issue #31)
validation-31-2026-04-04Verdict: PASS
Ticket
forgejo_admin/twitch-2k-wager#31 — Dev overlay: playme2k-dev Vite hot reload
Board item #803 on board-twitch-2k-wager
Environment
Production cluster (archbox), namespace: playme2k, Tailscale funnel: playme2k-dev.tail5b443a.ts.net
Checks
# Criterion How Verified Result Evidence 1 playme2k-dev pod running kubectl get pods -n playme2k PASS playme2k-dev-5b7ddf46b8-74hmx READY 1/1, STATUS Running 2 https://playme2k-dev.tail5b443a.ts.net returns 200 curl -sk -o /dev/null -w '%{http_code}' https://playme2k-dev.tail5b443a.ts.net PASS HTTP 200. Response body contains full PlayMe2K SvelteKit app HTML with Twitch-inspired dark theme CSS. 3 Vite dev server running kubectl get pods playme2k-dev -o jsonpath + kubectl logs PASS Container image: node:22, port: 5175 (Vite default). Logs show active HTTP request handling (404s for scanner probes, confirming Vite is serving). 4 Dev overlay exists in pal-e-deployments git show origin/main:overlays/twitch-2k-wager/dev/kustomization.yaml PASS Dev overlay present with deployment.yaml, service.yaml, ingress.yaml resources. Commit 2a41ce6 "feat: add playme2k dev overlay for Vite hot reload (#92)" 5 Tailscale funnel ingress resolves kubectl get ingress -n playme2k PASS playme2k-dev-funnel ingress exists, class tailscale, address playme2k-dev.tail5b443a.ts.net Regression Check
Dev overlay is independent of prod. Prod twitch-2k-wager deployment unaffected (its ImagePullBackOff is a pre-existing issue). Other namespaces unaffected.
Discovered Issues
None. Dev overlay is fully operational.
-
Validation: CI pipeline (issue #22)
validation-22-2026-04-04Verdict: PASS
Ticket
forgejo_admin/twitch-2k-wager#22 — CI pipeline: Woodpecker build + Harbor push
Board item #801 on board-twitch-2k-wager
Environment
Woodpecker CI, repo: forgejo_admin/twitch-2k-wager, pipeline #13
Checks
# Criterion How Verified Result Evidence 1 Pipeline #13 all 7 steps success mcp__woodpecker__get_pipeline_status(repo=forgejo_admin/twitch-2k-wager, number=13) PASS All 7 steps success (exit_code 0): clone, install, check, test, build, build-and-push, update-kustomize-tag. Event: push on main. Message: "fix: lazy Stripe client init (#35)" 2 Image pushed to Harbor Woodpecker build-and-push step logs PASS Pushed harbor.harbor.svc.cluster.local/playme2k/app:82768f26b658dd717e624645d6b1364d76a7f668 (sha256:1a878cbed8749b648b226d516f137dc9034fbd8d48ee48ef058bc7e63c521515) 3 Kustomize tag updated Woodpecker update-kustomize-tag step logs PASS Updated overlays/twitch-2k-wager/prod/kustomization.yaml newTag from "latest" to "82768f26b658dd717e624645d6b1364d76a7f668". Commit 32b4084 pushed to pal-e-deployments main. Regression Check
Pipeline is self-contained. No impact on other repos or pipelines.
Discovered Issues
- Note: CI pushes to harbor.harbor.svc.cluster.local (internal) but kustomize references harbor.tail5b443a.ts.net (external). Both should resolve to the same registry, but the Harbor project "playme2k" is not visible via the external API — this is a service-onboarding (issue #1) gap, not a CI gap. The CI pipeline itself did its job correctly.
-
Validation: Game status UI + email receipts
validation-13-2026-04-04Verdict: PARTIAL
Ticket
Issue #13 — Game status page with progress tracker, result banners, and 5-second polling. Board item #799.
Environment
Vite dev server at
https://playme2k-dev.tail5b443a.ts.net. Database not wired in dev overlay.Checks
# Criterion How Verified Result Evidence 1 /game/[id] route exists Filesystem check PASS src/routes/game/[id]/+page.svelte (317 lines) and +page.server.ts (93 lines) present. 2 Page server load function Code review PASS Validates game ID (parseInt, NaN check). Loads game, verifies session ownership (challenger_id matches twitchId). Fetches challenger, payment, payout in parallel via Promise.all. Serializes dates to ISO strings. 3 Progress tracker UI (5 steps) Code review PASS buildSteps() creates 5 steps: Payment Confirmed, Added to Queue, Game In Progress, Game Result, Payout. Each step has label, desc, time, and state (completed/active/pending). State derived from game.status progression. 4 Result banners for terminal states Code review PASS Win banner shows trophy icon, "You Won!", pot amount, payout processing message. Lose banner shows "Marcus Wins" with "run it back" message. Cancelled state handled separately. 5 5-second auto-polling Code review PASS onMount sets setInterval(invalidateAll, 5000) for non-terminal states. Cleared on terminal state or component destroy. Visual polling indicator with animated dot. 6 Status alerts per game state Code review PASS getAlert() returns type+icon+message for: pending (warning), paid (info), queued (info), playing (warning+gamepad), won (success+trophy), lost (null), cancelled (warning). 7 Payout status descriptions Code review PASS getPayoutDesc() handles: lost ("No payout"), cancelled ("Refund"), pending, onboarding ("Complete Stripe Connect setup"), initiated ("2-3 business days"), completed (amount), failed ("contact support"). 8 Match card with VS layout Code review PASS Marcus (Host) VS Challenger with avatar initials, username, role labels. Pot amount displayed. 9 /game/1 renders in browser Playwright navigate FAIL (expected) 500 Internal Error. Root cause: DATABASE_URL not configured — db.ts throws "DATABASE_URL environment variable is required". Also requires authenticated session (locals.session) for ownership check. 10 Test file exists Filesystem check PASS src/routes/game/[id]/__tests__/page-server.test.ts present 11 Email receipt module Filesystem check PASS src/lib/email.ts and src/lib/__tests__/email.test.ts present Regression Check
Game page error is isolated — landing page at / continues to load correctly. No regression.
Discovered Issues
No code bugs. Game status page requires: (1) DATABASE_URL configured, (2) authenticated session (Keycloak login), (3) valid game record in DB. All blocked by infra dependencies tracked in issue #21.
-
Validation: Kustomize overlay (issue #3)
validation-3-2026-04-04Verdict: PARTIAL
Ticket
forgejo_admin/twitch-2k-wager#3 — Kustomize overlay (pal-e-deployments)
Board item #788 on board-twitch-2k-wager
Environment
Production cluster (archbox), namespace: playme2k, ArgoCD application: playme2k
Checks
# Criterion How Verified Result Evidence 1 Kustomize overlay exists in pal-e-deployments git show origin/main:overlays/twitch-2k-wager/prod/kustomization.yaml PASS Full kustomization.yaml present on remote main. Contains deployment patches, service patches, Harbor image reference (harbor.tail5b443a.ts.net/playme2k/app), and tag 82768f26... 2 ArgoCD sync status is Synced kubectl get application playme2k -n argocd -o jsonpath='{.status.sync.status}' FAIL Status: OutOfSync. ArgoCD is not in a clean synced state. 3 ArgoCD health status is Healthy kubectl get application playme2k -n argocd -o jsonpath='{.status.health.status}' FAIL Status: Progressing. Deployment twitch-2k-wager has 0/1 available replicas due to ImagePullBackOff. 4 Target pods running new image kubectl get deployment -n playme2k -o wide FAIL twitch-2k-wager deployment READY 0/1. Image: harbor.tail5b443a.ts.net/playme2k/app:82768f26... — correct tag but pod cannot pull (missing Harbor project + harbor-creds secret). Regression Check
No regression to existing overlays. Only new overlay affected. Dev overlay (playme2k-dev) is healthy and unrelated to this prod overlay.
Discovered Issues
- Overlay kustomization is correct and tag was updated by CI, but the deployment cannot converge because upstream blockers (Harbor project missing, harbor-creds secret missing) prevent image pull. These are issue #1 scope, not #3 scope.
- The overlay itself is structurally correct — once Harbor/creds are fixed, ArgoCD should sync to Healthy.
-
Validation: Service onboarding (issue #1)
validation-1-2026-04-04Verdict: PARTIAL
Ticket
forgejo_admin/twitch-2k-wager#1 — Service onboarding: Keycloak + Twitch IdP + CNPG + funnel
Board item #786 on board-twitch-2k-wager
Environment
Production cluster (archbox), namespace: playme2k, Tailscale funnel: playme2k.tail5b443a.ts.net
Checks
# Criterion How Verified Result Evidence 1 Namespace playme2k exists kubectl get ns playme2k PASS NAME playme2k STATUS Active AGE 13h 2 Keycloak realm twitch-2k-wager exists Keycloak admin API GET /admin/realms/twitch-2k-wager PASS Realm: twitch-2k-wager, Enabled: True, DisplayName: Twitch 2K Wager 3 Harbor project playme2k exists Harbor API GET /api/v2.0/projects?name=playme2k (authenticated) FAIL Empty array returned. No playme2k project in Harbor. CI pushed via internal URL harbor.harbor.svc.cluster.local but project not created via Harbor API. 4 ArgoCD app exists kubectl get application -n argocd playme2k PASS Application exists, pointing to overlays/twitch-2k-wager/prod. Status: OutOfSync/Progressing (expected given Harbor issue). 5 Tailscale funnel resolves curl -sk https://playme2k.tail5b443a.ts.net FAIL HTTP 000 (connection refused). Prod pod in ImagePullBackOff, so funnel has no healthy backend. 6 CNPG database cluster exists kubectl get clusters.postgresql.cnpg.io -n playme2k FAIL No CNPG resources found in playme2k namespace. 7 harbor-creds secret in namespace kubectl get secret -n playme2k harbor-creds FAIL Error from server (NotFound): secrets "harbor-creds" not found. FailedToRetrieveImagePullSecret events on pod. Regression Check
No regression — this is a new namespace/service. Existing services unaffected.
Discovered Issues
- Harbor project playme2k does not exist — CI pushed via internal URL but the project was never created through Harbor API/terraform. Pods cannot pull the image externally.
- harbor-creds imagePullSecret missing in playme2k namespace — needs to be provisioned by terraform or sealed secret.
- CNPG database cluster not provisioned — ticket title mentions CNPG but no PostgreSQL cluster resource exists.
- Prod funnel not serving traffic due to cascading Harbor/image failures.
-
Validation: Stripe Connect payout + admin result
validation-12-2026-04-04Verdict: PARTIAL
Ticket
Issue #12 — Stripe Connect Express payout flow + admin game result endpoint. Board item #798.
Environment
Code review of repo at
~/twitch-2k-wager/. Runtime blocked — STRIPE_SECRET_KEY, ADMIN_SECRET not configured.Checks
# Criterion How Verified Result Evidence 1 POST /api/admin/result endpoint exists File read PASS src/routes/api/admin/result/+server.ts — 223 lines. Accepts {game_id, winner} body. ADMIN_SECRET Bearer auth. Validates game is in active status (paid/queued/playing). 2 Challenger win flow: Connect account creation Code review PASS handleChallengerWin(): First win creates Connect Express account via createConnectAccount(email), updates challenger connect status to 'pending', creates payout (status: onboarding), returns onboarding URL. 3 Challenger win flow: Already onboarded Code review PASS If connect_onboarding_status === 'complete' and account exists, creates transfer immediately via createTransfer(). Payout status: initiated. 4 Marcus win flow: Revenue split Code review PASS handleMarcusWin(): Game -> 'lost'. 50/50 revenue split recorded via recordRevenueSplit(). MARCUS_CUT_PCT = 50. 5 Connect helpers in stripe.ts File read PASS createConnectAccount() — Express type, transfers capability, manual payout schedule. getOnboardingLink() — account_onboarding type with return/refresh URLs. createTransfer() — amount in cents, USD, game_id metadata. 6 Admin auth guard Code review PASS requireAdmin() checks ADMIN_SECRET env var, validates Authorization Bearer header. Returns 401/403/500 on failures. 7 Webhook handles account.updated for onboarding completion Code review PASS handleAccountUpdated() checks charges_enabled + details_submitted, finds challenger by connect account ID, marks onboarding complete, processes pending payouts. 8 Test file exists Filesystem check PASS src/routes/api/admin/result/__tests__/result.test.ts present 9 Runtime admin endpoint N/A BLOCKED Cannot test — STRIPE_SECRET_KEY, ADMIN_SECRET, DATABASE_URL not configured in dev overlay. Regression Check
Admin endpoint is isolated — does not affect landing page or other routes. No regression on existing functionality.
Discovered Issues
No code bugs. Full Connect payout flow requires: (1) Stripe Connect enabled on account, (2) STRIPE_SECRET_KEY, ADMIN_SECRET, DATABASE_URL configured. Tracked by issue #21.
-
Validation: Stripe Checkout + webhook
validation-11-2026-04-04Verdict: PARTIAL
Ticket
Issue #11 — Stripe Checkout session creation + webhook handler for payment confirmation. Board item #797.
Environment
Code review of repo at
~/twitch-2k-wager/. Runtime blocked — STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET not configured in dev overlay.Checks
# Criterion How Verified Result Evidence 1 stripe.ts exists with Checkout session creation File read PASS 165 lines. createCheckoutSession() uses inline price_data (no pre-created Products). Three tiers: Bronze $15 (1500c), Silver $30 (3000c), Gold $45 (4500c). Metadata includes game_id, challenger_twitch_id, tier, wager_type, marcus_cut_pct. Success/cancel URLs point to /game/{id}. 2 POST /api/checkout endpoint exists File read PASS src/routes/api/checkout/+server.ts — 78 lines. Validates tier, twitch_id, twitch_username, email. Upserts challenger, creates game record, creates Stripe session, creates payment record. Returns checkout_url, game_id, session_id. 3 POST /api/webhooks/stripe endpoint exists File read PASS src/routes/api/webhooks/stripe/+server.ts — 209 lines. Handles checkout.session.completed (payment -> completed, game -> paid) and account.updated (Connect onboarding completion, triggers pending payout transfers). Signature verification via Stripe SDK. Idempotent handling. 4 Tier cards visible on landing page Playwright DOM snapshot PASS Bronze/Silver/Gold cards with buy-in, match, and winner-takes amounts. Silver has "Most Popular" badge. 5 Input validation on checkout endpoint Code review PASS Validates: required fields (tier, twitch_id, twitch_username, email), email format regex, tier in VALID_TIERS [15,30,45]. Returns 400 with descriptive error messages. 6 Webhook signature verification Code review PASS verifyWebhookSignature() uses stripe.webhooks.constructEvent(). Rejects missing stripe-signature header (400). Logs verification failures. 7 Test files exist Filesystem check PASS src/lib/__tests__/stripe.test.ts, src/routes/api/checkout/__tests__/checkout.test.ts, src/routes/api/webhooks/stripe/__tests__/webhook.test.ts all present. 8 Runtime checkout flow N/A BLOCKED Cannot test — STRIPE_SECRET_KEY not configured. Stripe client throws at module load without key. Regression Check
Landing page tier cards render correctly. Stripe module failure is isolated — does not affect page load (lazy import only triggered by API calls).
Discovered Issues
No code bugs. Runtime validation requires STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET env vars. Tracked by existing issue #21.
-
Validation: Keycloak OIDC auth (Twitch login)
validation-10-2026-04-04Verdict: PARTIAL
Ticket
Issue #10 — Keycloak OIDC auth with PKCE for Twitch login. Board item #796.
Environment
Vite dev server at
https://playme2k-dev.tail5b443a.ts.net. Keycloak env vars (KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID) not configured in dev overlay.Checks
# Criterion How Verified Result Evidence 1 "Login with Twitch" button exists and is clickable Playwright click PASS Button at ref=e9 with Twitch icon, cursor:pointer. Click navigates to /auth/login. 2 /auth/login route exists Playwright navigate + code read PASS src/routes/auth/login/+server.ts exists. GET handler generates PKCE verifier+challenge, sets httpOnly cookies, redirects to Keycloak authorize URL. 3 /auth/callback route exists Filesystem check PASS src/routes/auth/callback/+server.ts present 4 /auth/logout route exists Filesystem check PASS src/routes/auth/logout/+server.ts present 5 auth.ts OIDC helpers complete File read PASS 211 lines. Full PKCE flow: generateCodeVerifier (RFC 7636), generateCodeChallenge (S256), generateState, buildAuthorizeUrl, exchangeCode, fetchUserInfo, decodeJwtPayload, session encode/decode. Public client with PKCE (no client_secret). 6 Login redirects to Keycloak Playwright click "Login with Twitch" FAIL (expected) 500 Internal Error at /auth/login. Root cause: KEYCLOAK_URL env var not set. auth.ts throws "Missing environment variable: KEYCLOAK_URL" at startup. This is correct fail-fast behavior — not a code bug. 7 Test file exists Filesystem check PASS src/lib/__tests__/auth.test.ts present Regression Check
Landing page continues to load correctly after auth route errors — the 500 is contained to /auth/* routes. No regression on the main page.
Discovered Issues
No code bugs. The 500 on /auth/login is expected — Keycloak realm and Twitch IdP are not yet configured in this environment. Full end-to-end auth validation requires: (1) Keycloak realm created with Twitch IdP, (2) KEYCLOAK_URL/REALM/CLIENT_ID env vars set in dev overlay. Tracked by existing issue #21 (end-to-end flow validation).
-
Validation: Postgres schema + db.ts
validation-9-2026-04-04Verdict: PASS
Ticket
Issue #9 — Postgres schema, db.ts typed query layer, and types.ts. Board item #795.
Environment
Code review of repo at
~/twitch-2k-wager/src/lib/. Runtime DB validation blocked (no DATABASE_URL in dev overlay) — code-level validation only.Checks
# Criterion How Verified Result Evidence 1 schema.sql exists with all tables File read PASS 5 tables: challenger, game, payment, payout, revenue_split. All with proper constraints, CHECK clauses, and foreign keys. 2 db.ts exists with typed query helpers File read PASS 232 lines. Generic query<T> and queryOne<T> helpers. CRUD functions for all 5 entities: getChallenger, upsertChallenger, updateConnectStatus, createGame, getGame, updateGameStatus, listGamesByChallenger, listQueuedGames, createPayment, updatePaymentStatus, createPayout, updatePayoutStatus, recordRevenueSplit, closePool. 3 types.ts exists with TypeScript interfaces File read PASS 77 lines. Union types: ConnectOnboardingStatus, GameStatus, PaymentStatus, PayoutStatus, BuyInAmount. Interfaces: Challenger, Game, Payment, Payout, RevenueSplit. All monetary amounts documented as cents. 4 Schema/types alignment Manual comparison PASS All SQL columns map 1:1 to TypeScript interface fields. CHECK constraints match union types (e.g., game.status IN matches GameStatus union). buy_in_amount CHECK (1500,3000,4500) matches BuyInAmount type. 5 db.ts imports types correctly File read PASS Imports Challenger, Game, Payment, Payout, RevenueSplit from ./types.js 6 Pool configuration File read PASS Uses pg.Pool with DATABASE_URL env var. Fail-fast if missing. closePool() for graceful shutdown. 7 Test file exists Filesystem check PASS src/lib/__tests__/db.test.ts present Regression Check
No prior DB layer existed — this is the initial implementation. Schema is self-consistent with proper foreign key references (game references challenger, payment/payout/revenue_split reference game).
Discovered Issues
None. Runtime DB connection cannot be validated until DATABASE_URL is configured in the dev overlay — this is an infra dependency, not a code issue.
-
Validation: SvelteKit scaffold + Dockerfile
validation-8-2026-04-04Verdict: PASS
Ticket
Issue #8 — SvelteKit scaffold with Dockerfile, dark theme landing page, tier cards, and mobile layout. Board item #794.
Environment
Vite dev server at
https://playme2k-dev.tail5b443a.ts.net. Validated via Playwright browser automation.Checks
# Criterion How Verified Result Evidence 1 Landing page loads at root URL Playwright navigate to / PASS Page title: "PlayMe2K — Challenge Marcus in NBA 2K for Cash". Status 200, no errors. 2 Dark theme applied Full-page screenshot PASS Screenshot confirms dark background (#0d0d0d range), purple accent colors, white text. Professional dark theme throughout. 3 Tier cards visible (Bronze $15, Silver $30, Gold $45) DOM snapshot inspection PASS Three tier cards rendered: Bronze ($15, pot $30), Silver ($30, pot $60, "Most Popular" badge), Gold ($45, pot $90). Buy-in/match/winner breakdown on each. 4 Mobile layout responsive Playwright resize to 375x812 (iPhone) PASS Mobile screenshot confirms single-column layout, full-width tier cards, readable text, properly stacked sections. No horizontal overflow. 5 Scaffold files exist (Dockerfile, svelte.config.js, vite.config.ts, package.json) Filesystem check PASS All four files present at ~/twitch-2k-wager/ 6 SvelteKit routes exist (+page.svelte, +layout.svelte, app.html) Filesystem check PASS All core SvelteKit files present at src/routes/ and src/ 7 No console errors on landing page Playwright console_messages(level=error) PASS 0 errors on initial page load 8 "Login with Twitch" button visible DOM snapshot PASS Button with Twitch icon rendered, cursor:pointer, clickable 9 How It Works section (5 steps) DOM snapshot PASS 5 numbered steps: Sign In, Pick Your Wager, Pay & Queue Up, Play Live on Stream, Winner Takes the Pot 10 FAQ section present DOM snapshot PASS 4 questions: fairness, disconnects, cancellation, console/platform 11 Footer with Pal Enterprises branding DOM snapshot PASS "PlayMe2K is a Pal Enterprises product." with Terms/Privacy links Regression Check
Landing page is the entry point — no prior pages to regress. All sections render correctly on both desktop (1280x800) and mobile (375x812).
Discovered Issues
None.
-
Review: Dev overlay playme2k-dev Vite hot reload
review-803-2026-04-03Verdict: READY
Re-review after body fixes. Previous flags (hostPath prerequisite not documented, prod overlay relationship) both resolved.
Template Completeness
- [x] Type — Feature
- [x] Lineage — references sop-frontend-dev-overlay, notes independence from prod overlay #3
- [x] Repo — forgejo_admin/pal-e-deployments
- [x] User Story — platform operator dev iteration story
- [x] Context — references SOP, port convention, namespace coexistence, hostPath prerequisite
- [x] File Targets — 4 files in overlays/twitch-2k-wager/dev/
- [x] Acceptance Criteria — 4 criteria, all testable
- [x] Test Expectations — phone + hot reload verification
- [x] Constraints — 7 constraints including hostPath prerequisite, port 5175, memory limit 512Mi
- [x] Checklist — PR, tests, no unrelated changes
- [x] Related — SOP, project page, arch note, prod overlay #3
Traceability
- [x] story:challenger-auth — Challenger Twitch Login. Story note verified: story-twitch-2k-wager-challenger-auth (id 1157) exists, listed in project-twitch-2k-wager user-stories table
- [x] arch:deployment-twitch-2k-wager — Deployment architecture. Arch note verified: arch-deployment-twitch-2k-wager (id 1156) exists, status active
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#31, state: open
File Targets
- [x] overlays/twitch-2k-wager/dev/kustomization.yaml — to create. Parent dir does not exist yet (expected — this ticket creates the dev/ subdirectory). Pattern matches westsidekingsandqueens/dev/ and mcd-tracker-app/dev/ reference overlays
- [x] overlays/twitch-2k-wager/dev/deployment.yaml — to create. node:22, hostPath to ~/twitch-2k-wager (confirmed exists on archbox), port 5175, emptyDir for node_modules. Command must pass --port 5175 since package.json defaults to port 3000
- [x] overlays/twitch-2k-wager/dev/service.yaml — to create. port 80 to targetPort 5175
- [x] overlays/twitch-2k-wager/dev/ingress.yaml — to create. Tailscale funnel: playme2k-dev.tail5b443a.ts.net
Targets are specific enough for an agent to act on without guessing. Two validated reference overlays provide exact patterns to follow.
Repo Placement
OK. Issue filed on twitch-2k-wager repo, work targets pal-e-deployments. This is the standard pattern — matches how prod overlay #3 was handled. Single repo target, no multi-repo concern.
Dependencies
- [x] #3 (prod kustomize overlay) — satisfied. Closed/merged. Dev overlay creates separate dev/ subdirectory alongside prod/. No conflict.
- [x] #8 (SvelteKit scaffold) — satisfied. Merged. ~/twitch-2k-wager has full scaffold (src/, package.json with dev script, vite.config.ts).
- [x] playme2k namespace — satisfied. Active in k8s (created by service onboarding). Prod pods already running there.
- [x] ~/twitch-2k-wager on archbox — satisfied. Directory exists with full SvelteKit project.
- [x] sop-frontend-dev-overlay — satisfied. SOP exists and documents the pattern.
No unresolved dependencies.
Acceptance Criteria
- [x] "kubectl apply -k succeeds" — testable, specific command
- [x] "playme2k-dev URL serves Vite dev server" — testable via curl or browser
- [x] "Editing .svelte files triggers hot reload" — testable via file edit + URL check
- [x] "Dev and prod pods coexist in playme2k namespace" — testable via kubectl get pods
All 4 criteria are testable and specific. No ambiguous language.
Blast Radius
Low. Creates 4 new files in a new subdirectory. No existing files modified. No playme2k references exist in pal-e-deployments yet (confirmed via grep). Dev overlay is fully isolated from prod — separate deployment name, separate Tailscale funnel hostname, separate service. Rollback is trivial: kubectl delete -k.
Decomposition Assessment
4 files in 1 repo. 4 acceptance criteria. All files follow an established pattern with two validated reference implementations. Estimated agent time: under 5 minutes. No independent subtasks that need parallelization — all 4 files are created together as a unit.
No decomposition needed.
Recommendation
No action needed. Ticket is ready for dispatch.
-
Review: CI pipeline: Woodpecker build + Harbor push
review-801-2026-04-04Verdict: NEEDS_REFINEMENT
Re-review of board item #801 (Forgejo issue #22). Previous review flagged 4 body issues -- all confirmed fixed. This re-review found 1 new issue: Harbor image path mismatch.
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- prerequisite for #21, depends on #3
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- clear developer story about CI automation
- [x] Context -- references existing platform CI pattern with specific repos
- [x] File Targets -- .woodpecker.yaml to create, Dockerfile listed as do-not-touch
- [x] Acceptance Criteria -- 7 testable criteria
- [x] Test Expectations -- 4 verification steps
- [x] Constraints -- pattern matching, tag format, credentials, overlay dependency, token
- [x] Checklist -- PR opened, tests pass, no unrelated changes
- [x] Related -- project page, arch note, dependency #3, dependent #21
Traceability
- [x] story:challenger-auth label -- Challenger Twitch Login
- [x] story note verified -- found in project-twitch-2k-wager user-stories table (key: challenger-auth, note: story-twitch-2k-wager-challenger-auth id 1157)
- [x] arch:deployment-twitch-2k-wager label -- Deployment architecture
- [x] arch note verified -- arch-deployment-twitch-2k-wager (id 1156) exists with full Mermaid diagram including ArgoCD + Kustomize components
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#22, open
File Targets
- [x]
.woodpecker.yaml-- to create. Verified does NOT exist yet in repo. - [x]
Dockerfile-- listed as do-not-touch. Verified EXISTS in repo (from #8, closed/merged).
Targets are specific enough for an agent to act on. Reference pattern in basketball-api/.woodpecker.yaml is explicitly called out in Context.
Repo Placement
OK. Issue filed on
forgejo_admin/twitch-2k-wager. Primary file target (.woodpecker.yaml) belongs in same repo. Theupdate-kustomize-tagstep writes topal-e-deploymentsbut uses the shared platform script downloaded at runtime -- no cross-repo PR needed.Dependencies
- [x] #3 (kustomize overlay) -- satisfied. Issue closed. Overlay at
overlays/twitch-2k-wager/prod/kustomization.yamlconfirmed to exist in pal-e-deployments. - [x] #8 (SvelteKit scaffold + Dockerfile) -- satisfied. Issue closed. Dockerfile confirmed to exist in repo.
- [x] #21 (validation) -- depends on this ticket. Correctly documented in Lineage. No blocker.
Acceptance Criteria
7 AC, all testable. Agent can verify programmatically:
- AC 1-3: file content inspection + Woodpecker pipeline trigger
- AC 4: Harbor image check (but path is wrong -- see Blast Radius)
- AC 5: kustomization.yaml update verification
- AC 6: Woodpecker secret activation via MCP tool
- AC 7: end-to-end pipeline run verification
No ambiguous language. All criteria are specific and observable.
Blast Radius
Harbor image path mismatch. AC #4 says image pushed to
harbor.tail5b443a.ts.net/playme2k/app:${CI_COMMIT_SHA}. But the kustomization.yaml in pal-e-deployments defines the image asharbor.tail5b443a.ts.net/twitch-2k-wager/app. The Harbor project istwitch-2k-wager, notplayme2k. An agent following the issue body would configure Kaniko to push to the wrong Harbor project. ArgoCD would never find the image, causing a failed deployment.The reference pattern in
basketball-api/.woodpecker.yamlusesbasketball-api/apias the Harbor repo (matching the overlay directory name), confirming the convention is{overlay-name}/{image-name}. Correct path:twitch-2k-wager/app.Rollback is straightforward -- revert the .woodpecker.yaml commit. No data migration or state change involved.
Decomposition Assessment
Apply three-thing limit and five-minute rule:
- 1 file target in 1 repo -- well under the 3-file threshold
- 7 AC but all focused on a single
.woodpecker.yamlconfiguration plus Woodpecker secret activation via MCP tool - No independent subtasks that need parallelization -- this is a single coherent unit
- Estimated agent time: under 5 minutes
No decomposition needed.
Recommendation
- [BODY] Fix Harbor image path in AC #4:
harbor.tail5b443a.ts.net/playme2k/app:${CI_COMMIT_SHA}toharbor.tail5b443a.ts.net/twitch-2k-wager/app:${CI_COMMIT_SHA}
-
Review: CI pipeline: Woodpecker build + Harbor push
review-801-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — "Prerequisite for #21 (validation). Enables automated builds."
- [x] Repo —
forgejo_admin/twitch-2k-wager - [x] User Story — clear developer-facing story for CI automation
- [x] Context — references existing patterns, names Harbor project, links to sibling repos
- [x] File Targets —
.woodpecker.yamlto create;Dockerfileexplicitly excluded - [x] Acceptance Criteria — 6 testable items
- [x] Test Expectations — 3 items covering trigger, completion, and image visibility
- [x] Constraints — pattern matching, Harbor credentials, Woodpecker activation noted
- [x] Checklist — standard 3-item checklist
- [x] Related — project page, arch note, downstream issue, SOP linked
All template sections present and filled.
Traceability
- [x] story:challenger-auth label — Challenger Twitch Login
- [x] story note verified —
story-twitch-2k-wager-challenger-auth(id 1157) exists in pal-e-docs;challenger-authkey listed inproject-twitch-2k-wageruser-stories table - [x] arch:deployment-twitch-2k-wager label — Deployment architecture
- [x] arch note verified —
arch-deployment-twitch-2k-wager(id 1156) exists in pal-e-docs with full Mermaid diagram and component table - [x] Forgejo issue — forgejo_admin/twitch-2k-wager#22, state: open
Traceability triangle complete. All backing notes exist.
File Targets
- [x]
.woodpecker.yaml— confirmed does NOT exist in repo (create target valid). Repo root contains: Dockerfile, package.json, src/, svelte.config.js, etc. - [x]
Dockerfile— confirmed exists in repo (do-not-touch constraint valid, created by #8)
Targets are specific. Agent knows exactly what to create and what not to touch.
Repo Placement
OK. Issue filed on
forgejo_admin/twitch-2k-wager, file target (.woodpecker.yaml) belongs in the same repo. No cross-repo file changes needed. Theupdate-kustomize-tagstep (see Dependencies) modifies pal-e-deployments at runtime via script, not as a code change in this PR.Dependencies
- [x] #21 (Validation: end-to-end flow) — documented downstream dependency (depends on this ticket). Open. Correct.
- [x] #8 (SvelteKit scaffold + Dockerfile) — board item #794, in
needs_approval. Provides the Dockerfile this pipeline builds. Implicitly referenced ("Dockerfile already exists from #8") but not listed as a blocking dependency. - [ ] #3 (Kustomize overlay in pal-e-deployments) — board item #788, in
needs_approval. UNDOCUMENTED DEPENDENCY. Reference pipelines (basketball-api, westside-app) all include anupdate-kustomize-tagstep that pushes the new image tag to the pal-e-deployments kustomize overlay. This step requires the overlay directory to exist. The issue does not mention this step or this dependency.
Acceptance Criteria
6 AC items evaluated:
- [x] ".woodpecker.yaml follows existing platform CI pattern" — testable by diff against reference pipelines
- [x] "Pipeline triggers on push to main" — testable via Woodpecker MCP tools
- [x] "Docker image builds using existing Dockerfile" — testable via pipeline status
- [ ] "Image pushed to playme2k/app:latest and playme2k/app:{commit-sha}" — ISSUE: reference pipelines (basketball-api, westside-app) only tag with
${CI_COMMIT_SHA}. No:latesttag. Either AC is wrong or this is an intentional deviation that should be documented in Constraints. - [x] "Woodpecker repo activated with Harbor robot credentials as secrets" — testable via mcp__woodpecker tools
- [x] "First pipeline run succeeds" — testable via pipeline trigger + status check
Missing AC: No criterion for the
update-kustomize-tagstep. Every platform pipeline includes this step (downloads shared script from pal-e-platform, pushes new tag to pal-e-deployments overlay). This is a standard platform pattern omitted from scope.Blast Radius
Low.
.woodpecker.yamlis self-contained to this repo. Harbor projectplayme2kand robot accounts already provisioned per pal-e-services PR #54. No downstream services consume this image yet (kustomize overlay not yet deployed). No sibling services affected. Rollback is trivial (revert one file).Decomposition Assessment
Apply three-thing limit and five-minute rule:
- Discrete changes: 1 file to create + Woodpecker repo activation + secret setup = 3 things. At the limit but acceptable.
- Estimated agent time: Under 5 minutes. Standard CI onboarding pattern, well-documented by reference pipelines.
- Parallelizable subtasks: None — file creation, activation, and verification are sequential.
No decomposition needed.
Recommendation
[BODY]Fix AC: remove:latesttag reference or explicitly add:latestas a second Kaniko tag and document the deviation from reference pipelines in Constraints.[BODY]Add AC forupdate-kustomize-tagstep: "Pipeline includes update-kustomize-tag step that pushes image tag to pal-e-deployments overlay."[BODY]Add dependency on #3 (Kustomize overlay) in Lineage or Related section. Theupdate-kustomize-tagstep will fail if the overlay directory does not exist in pal-e-deployments.[BODY]Add constraint: "Must include update-kustomize-tag step matching platform pattern (downloads shared script from pal-e-platform, requires OVERLAY and IMAGE_TAG env vars, depends_on build-and-push)."
-
Review: Game status UI + email receipts (re-review)
review-799-2026-04-03-v2Verdict: READY
Re-review after body + label fixes from first review. All previous issues resolved (arch label corrected, email sender decided). Scope is solid.
Template Completeness
- [x] Type — Feature
- [x] Lineage — Sub-ticket of #7, depends on #10 + #11
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — Challenger wants real-time game status + email receipts
- [x] Context — Wires game status page to real DB state, poll for updates, email receipts on completion. Dedicated Gmail per project convention.
- [x] File Targets — 3 files listed with descriptions
- [x] Acceptance Criteria — 5 testable criteria
- [x] Test Expectations — 2 test items with correct test email target
- [x] Constraints — Email convention + poll preference documented
- [x] Checklist — Standard 3-item checklist
- [x] Related — Parent, dependencies, story notes, arch note all linked
Traceability
- [x] story:game-status — Game Status Visibility
- [x] story note verified — found in project-twitch-2k-wager user-stories table + standalone note story-twitch-2k-wager-game-status (id 1159)
- [x] story:winner-payout — Winner Payout and Receipt
- [x] story note verified — found in project-twitch-2k-wager user-stories table + standalone note story-twitch-2k-wager-winner-payout (id 1160)
- [x] arch:dataflow-twitch-2k-wager — Data Flow: Twitch 2K Wager
- [x] arch note verified — arch-dataflow-twitch-2k-wager (id 1155) exists, active, covers status update flow + email receipts in sequence diagram
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#13, open
File Targets
- [x]
src/routes/game/[id]/+page.server.ts— does not exist yet (will be CREATED). Parent directory exists. Valid SvelteKit convention for server-side load function. - [x]
src/routes/game/[id]/+page.svelte— EXISTS. Currently has placeholder/hardcoded data. Confirmed needs update to use real data + auto-refresh. - [x]
src/lib/email.ts— does not exist yet (will be CREATED). Parent directorysrc/lib/exists. New email module for project-scoped Gmail OAuth.
Targets are specific enough for an agent to act on without guessing.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, all file targets are in that repo. Single-repo scope. No cross-repo work needed.
Dependencies
- [x] #10 (Keycloak OIDC auth) — satisfied (issue closed)
- [ ] #11 (Stripe Checkout + webhook) — pending (open, in next_up column). BLOCKING: #13 requires Stripe webhook to create Game records with payment status.
- [x] #9 (Postgres schema + db.ts) — satisfied (db.ts exists in repo, issue in needs_approval)
- [x] #7 (parent umbrella) — non-blocking parent ticket, open
Blocking dependency on #11 is correctly documented in the issue body ("Depends on #10 + #11. Last in sequence"). Ticket cannot be dispatched until #11 merges.
Acceptance Criteria
5 AC — all testable by an agent:
- [x] Game status page state progression — verifiable by checking rendered UI against DB state
- [x] Auto-refresh via 5s poll — verifiable via setInterval/setTimeout in code review
- [x] Winner email content — verifiable by checking email template string
- [x] Loser email content — verifiable by checking email template string
- [x] Mobile support — verifiable via responsive CSS / existing app.css patterns
Test expectations (2 items) are appropriately scoped. Email test target (draneylucas@gmail.com) correct per convention.
Blast Radius
- 3 files touched, 1 repo, 1 service
- Email module is new and project-scoped — no cross-project impact
- Game status page is an existing route being upgraded from placeholder to real data — no new routes
- Rollback: revert PR reverts all changes cleanly
Decomposition Assessment
Apply the three-thing limit and five-minute rule:
- 3 file targets in 1 repo — at the limit, not over
- 5 AC — at the limit, not over
- Estimated agent time: ~3-4 minutes
- Work is tightly coupled: UI reads DB state, email sends on same game-completion event. Splitting would create artificial seams.
No decomposition needed.
Recommendation
No action needed. Previous review issues (arch label mismatch, undecided email sender) have been resolved. Ticket is ready for dispatch once #11 (Stripe Checkout + webhook) completes.
-
Review: Stripe Connect payout + admin result (re-review)
review-798-2026-04-03-r2Verdict: APPROVED
Re-review of board item #798 (forgejo_admin/twitch-2k-wager#12). Previous review flagged missing webhook file target and missing #9 dependency reference. Both issues resolved in updated issue body.
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Sub-ticket of #7. Depends on #9 + #11. Sequential.
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- winning challenger / Stripe payout / automatic future payouts
- [x] Context -- explains admin result flow, Connect Express deferred onboarding, revenue split
- [x] File Targets -- 4 files listed with clear descriptions
- [x] Acceptance Criteria -- 6 criteria covering both challenger-win and marcus-win paths
- [x] Test Expectations -- Stripe test mode, webhook delivery
- [x] Constraints -- Connect dashboard setting, test keys, auth requirement
- [x] Checklist -- standard PR/tests/no-unrelated
- [x] Related -- parent, dependencies, story note, arch note all linked
All required sections present. Template complete.
Traceability
- [x] story:winner-payout -- Winner Payout and Receipt
- [x] story note verified -- story-twitch-2k-wager-winner-payout exists (id 1160), listed in project-twitch-2k-wager user-stories table
- [x] arch:dataflow-twitch-2k-wager -- Data Flow: Twitch 2K Wager
- [x] arch note verified -- arch-dataflow-twitch-2k-wager exists (id 1155), active, includes Connect onboarding + transfer sequence
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#12, open
All three traceability legs verified with backing notes.
File Targets
- [x]
src/routes/api/admin/result/+server.ts-- NEW file. Parent dir does not exist yet (SvelteKit creates on first route). Correct: this ticket creates it. - [x]
src/routes/payout/+page.svelte-- NEW file. Parent dir does not exist yet. Correct: this ticket creates it. - [x]
src/lib/stripe.ts-- EXTENDS file created by #11. Does not exist on main yet. Correct: #11 (dependency) creates it, this ticket adds Connect helpers. - [x]
src/routes/api/webhooks/stripe/+server.ts-- EXTENDS file created by #11. Does not exist on main yet. Correct: #11 creates checkout.session.completed handler, this ticket adds account.updated handler.
All file targets valid. Two new files, two extensions of #11 deliverables. Previous review flagged missing webhook target -- now present.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager. All file targets within that repo. Single-repo scope. No cross-repo work needed.
Dependencies
- [x] #9 (Postgres schema + db.ts) -- SATISFIED. Issue closed. Board item #795 in needs_approval. Previous review flagged this as undocumented -- now listed in Lineage.
- [x] #11 (Stripe Checkout + webhook) -- PENDING. Issue open. Board item #797 in next_up. Creates stripe.ts and webhook endpoint that #12 extends. Sequential ordering documented.
- [x] #7 (parent) -- OPEN. Board item #793 in backlog. Parent decomposition ticket. No blocking relationship.
Dependencies correctly documented and sequenced. #12 cannot start until #11 merges.
Acceptance Criteria
6 criteria, all testable:
- [x] Admin endpoint accepts game_id + winner -- testable via HTTP request
- [x] First-time challenger win: Connect Express account created -- verifiable via Stripe test mode API
- [x] account.updated webhook triggers transfer -- verifiable via Stripe webhook test CLI
- [x] Repeat winner: transfer without re-onboarding -- verifiable via Stripe test mode
- [x] Marcus wins: RevenueSplit record created -- verifiable via DB query
- [x] Payout status tracked in DB -- verifiable via DB query
All criteria are concrete, specific, and programmatically verifiable. No ambiguity.
Blast Radius
Low. New feature in a new codebase. No sibling services share these patterns. The webhook endpoint extension (#11 to #12) is the only overlap point and is correctly scoped with "extends existing file" notation. Stripe Connect is isolated to this project. Rollback is straightforward -- revert the single PR.
Decomposition Assessment
4 file targets in 1 repo. 6 acceptance criteria. All within the same SvelteKit app.
- Three-thing limit: 3 discrete changes (admin endpoint, payout page, Connect helpers/webhook). Passes.
- Five-minute rule: estimated 3-4 minutes for an agent. Passes.
- No independent subtasks that benefit from parallelization -- all changes are tightly coupled (admin declares result, which triggers Connect flow, which uses webhook).
No decomposition needed.
Recommendation
No action needed. Both issues from previous review are resolved:
- Webhook file target (
src/routes/api/webhooks/stripe/+server.ts) now listed in File Targets with "extends existing file from #11" annotation. - #9 dependency now documented in Lineage section.
Ticket is ready for execution once #11 merges.
-
Review: Stripe Connect payout + admin result
review-798-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Sub-ticket of #7, depends on #11. Sequential.
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- winning challenger receives payout via Stripe after one-time setup
- [x] Context -- Connect Express deferred onboarding, account.updated webhook, revenue split
- [x] File Targets -- 3 files listed (all new)
- [x] Acceptance Criteria -- 6 items
- [x] Test Expectations -- 2 items
- [x] Constraints -- 3 items (Connect enabled, test keys, auth required)
- [x] Checklist -- standard PR/tests/no-unrelated
- [x] Related -- parent #7, dependency #11, story note, arch note
Traceability
- [x] story:winner-payout label -- Winner Payout and Receipt
- [x] story note verified -- story-twitch-2k-wager-winner-payout (id 1160) exists in pal-e-docs; winner-payout row present in project-twitch-2k-wager user-stories table
- [x] arch:dataflow-twitch-2k-wager label -- Data Flow: Twitch 2K Wager
- [x] arch note verified -- arch-dataflow-twitch-2k-wager (id 1155) exists in pal-e-docs
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#12, open
File Targets
- [x]
src/routes/api/admin/result/+server.ts-- new file. Parent dirsrc/routes/exists in scaffold. Correct SvelteKit API route placement. - [x]
src/routes/payout/+page.svelte-- new file. Parent dirsrc/routes/exists. Connect onboarding redirect/status page. - [x]
src/lib/stripe.ts-- new file. Parent dirsrc/lib/exists. Stripe helper module for Connect account creation + transfer. - [ ] MISSING: webhook route for
account.updated-- AC #3 requires handlingaccount.updatedwebhook events to detect onboarding completion and trigger transfer. No webhook file is listed in File Targets. If #11 creates a webhook endpoint (e.g.src/routes/api/webhook/+server.ts), this ticket must list it as a file to modify.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, all file targets are in that repo. Single-repo change. No cross-repo concerns.
Dependencies
- [x] #11 (Stripe Checkout + webhook) -- PENDING. Currently in
next_upon the board (item #797). Sequential dependency documented in Lineage. This ticket cannot start until #11 merges because it needs Stripe client initialization and webhook infrastructure. - [x] #7 (parent) -- open. Decomposed parent ticket in
backlog(item #793). Informational, not blocking. - [ ] #9 (Postgres schema + db.ts) -- UNDOCUMENTED dependency. Currently in
needs_approval(item #795). The Payout and RevenueSplit records from AC #5-6 require DB tables and types. Not listed in Lineage. - [x] #13 (Game status UI + email receipts) -- downstream consumer, not blocking. Shares story:winner-payout label.
Acceptance Criteria
6 criteria, all verifiable via Stripe test mode. Maps cleanly to the arch-dataflow-twitch-2k-wager sequence diagram (operator declares winner, Connect account created, transfer initiated). AC is a focused subset of the user story's 8 criteria -- email receipts and payout status UI are correctly deferred to #13.
Testability assessment:
- AC 1 (admin endpoint) -- testable with HTTP request + DB assertion
- AC 2 (Connect account creation) -- testable with Stripe test mode API
- AC 3 (account.updated webhook) -- testable with Stripe CLI webhook forwarding or test event simulation
- AC 4 (repeat winner transfer) -- testable with pre-onboarded test account
- AC 5 (RevenueSplit) -- testable with DB assertion
- AC 6 (payout status tracking) -- testable with DB query
All criteria are specific and agent-verifiable. No ambiguous language.
Blast Radius
Greenfield SvelteKit app with no downstream consumers yet. No shared libraries. No sibling services affected. The Stripe Connect integration is self-contained. Rollback is straightforward (revert the PR). The only cross-cutting concern is that the webhook endpoint created by #11 may need to handle both
checkout.session.completedandaccount.updatedevents -- this is an integration point between #11 and #12.Decomposition Assessment
3 file targets (+1 missing webhook file = 4 total), 1 repo, 6 AC. Within the three-thing limit (all changes are tightly coupled: admin endpoint calls stripe helpers, payout page shows Connect status). Estimated agent time: 3-4 minutes. No independent subtasks that could be parallelized -- the admin endpoint, stripe helpers, and payout page are interdependent. No decomposition needed.
Recommendation
[BODY]Add webhook route to File Targets: theaccount.updatedwebhook handler file (path TBD after #11 merges, likelysrc/routes/api/webhook/+server.ts) must be listed since AC #3 depends on extending it.[BODY]Add #9 (Postgres schema + db.ts) as a dependency in the Lineage section. Payout and RevenueSplit DB records require the schema and types from #9.
-
Review: Game status UI + email receipts
review-799-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Sub-ticket of #7, depends on #10 + #11
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- clear challenger wants real-time status + email receipts
- [x] Context -- sufficient background referencing scaffold (#8) and DB state
- [x] File Targets -- 3 specific paths listed
- [x] Acceptance Criteria -- 5 testable conditions
- [x] Test Expectations -- 2 expectations with test email target
- [x] Constraints -- email sender question + poll vs SSE decision noted
- [x] Checklist -- standard PR/tests/no-unrelated
- [x] Related -- parent #7, story notes, arch note linked
Traceability
- [x] story:game-status -- "Game Status Visibility" (story-twitch-2k-wager-game-status, id 1159)
- [x] story note verified -- found in project-twitch-2k-wager user-stories table
- [x] story:winner-payout -- "Winner Payout and Receipt" (story-twitch-2k-wager-winner-payout, id 1160)
- [x] story note verified -- found in project-twitch-2k-wager user-stories table
- [ ] arch:deployment-twitch-2k-wager -- MISMATCH. Note exists (id 1156) but this ticket is about game status UI + email sending, not deployment topology. The issue body itself references arch-dataflow-twitch-2k-wager in its Related section. Label should be arch:dataflow-twitch-2k-wager or arch:twitch-2k-wager-app.
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#13, open
File Targets
- [x]
src/routes/game/[id]/+page.server.ts-- does not exist yet (to be created). Parent directory exists with +page.svelte from scaffold (#8). Correct target. - [x]
src/routes/game/[id]/+page.svelte-- EXISTS in repo from scaffold (#8). Will be updated with real data + auto-refresh. Correct. - [x]
src/lib/email.ts-- does not exist yet (to be created). src/lib/ directory exists with db.ts, schema.sql, types.ts. Correct target.
Targets are specific enough for an agent to act without guessing.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, all file targets are in that repo. Single-repo change. No multi-repo concerns.
Dependencies
- [ ] #10 Keycloak OIDC auth (Twitch login) -- OPEN, in next_up column. Ticket Lineage declares dependency. NOT satisfied.
- [ ] #11 Stripe Checkout + webhook -- OPEN, in next_up column. Ticket Lineage declares dependency. NOT satisfied.
- [x] #8 SvelteKit scaffold + Dockerfile -- in needs_approval column. Files confirmed present in repo (game/[id]/+page.svelte exists). Effectively satisfied.
- [x] #9 Postgres schema + db.ts -- in needs_approval column. Files confirmed present in repo (db.ts, schema.sql exist). Effectively satisfied.
Blocking: This ticket CANNOT be dispatched until #10 and #11 are complete. Both are in next_up, not done. This is correctly documented in the Lineage section ("Last in sequence").
Acceptance Criteria
5 AC, all testable:
- [x] "Game status page shows: payment confirmed -> game queued -> in progress -> result -> payout status" -- testable via DB state manipulation + page load assertion
- [x] "Status updates without manual page refresh (poll every 5s or SSE)" -- testable via code inspection for setInterval/SSE
- [x] "Winner email" content -- testable via email send to draneylucas@gmail.com
- [x] "Loser email" content -- testable via email send to draneylucas@gmail.com
- [x] "Works on mobile" -- needs manual or viewport test, acceptable for feature ticket
AC are specific and testable. No ambiguous language.
Blast Radius
Low. Greenfield app with no downstream consumers. The email module (src/lib/email.ts) is new and self-contained. No sibling services share game status patterns. Rollback is straightforward (revert single PR). Only cross-cutting concern: which Gmail account sends twitch-2k-wager emails -- per feedback_email_architecture.md, email is per-project via gmail-sdk.
Decomposition Assessment
Apply the three-thing limit and five-minute rule:
- 3 file targets in 1 repo -- at the limit but not over
- 5 acceptance criteria -- at the limit but not over
- 2 discrete subsystems: (1) game status polling UI, (2) email sending -- these are coupled (email fires on game completion which is displayed on status page)
- Estimated agent work: ~5 minutes -- borderline
No decomposition needed. The two subsystems are tightly coupled (game result triggers both status display and email). Splitting would create artificial coordination overhead.
Recommendation
- [LABEL] Fix arch label on board item #799: change
arch:deployment-twitch-2k-wagertoarch:dataflow-twitch-2k-wager. The ticket is about game status data flow and email sending, not deployment topology. The issue body itself references arch-dataflow-twitch-2k-wager. - [SCOPE] Resolve email sender: the Constraints section says "check if westsidebasketball@gmail.com Gmail OAuth works for this, or if we need a separate sender." Per platform convention (feedback_email_architecture.md), email is per-project via gmail-sdk. Decision needed: which Gmail account sends twitch-2k-wager emails? Agent cannot execute with an open question in constraints.
- [BODY] Once email sender is decided, update the Constraints section to state the decision definitively (e.g., "Email sent via {account} using gmail-sdk") and clarify the src/lib/email.ts file target description accordingly.
-
Review: Stripe Checkout + webhook
review-797-2026-04-03Verdict: READY
Template Completeness
- [x] Type — Feature
- [x] Lineage — Sub-ticket of #7, depends on #8 + #9, parallel with #10, uses #2 spike
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — challenger wants tiered pay via Stripe Checkout
- [x] Context — Stripe Checkout with inline price_data, webhook handler, test keys
- [x] File Targets — 3 new files specified
- [x] Acceptance Criteria — 6 criteria
- [x] Test Expectations — Stripe CLI listen + DB verification
- [x] Constraints — test keys, match spike, webhook reachability
- [x] Checklist — PR opened, tests pass, no unrelated changes
- [x] Related — parent #7, spike #2, story note, arch note
Traceability
- [x] story:challenger-pay — "Secure Buy-In Payment" (story-twitch-2k-wager-challenger-pay, id 1158)
- [x] story note verified — challenger-pay entry found in project-twitch-2k-wager user-stories table
- [x] arch:dataflow-twitch-2k-wager — "Data Flow: Twitch 2K Wager" (arch-dataflow-twitch-2k-wager, id 1155)
- [x] arch note verified — arch-dataflow-twitch-2k-wager exists (note_type: architecture, status: active)
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#11, state: open
File Targets
- [x]
src/routes/api/checkout/+server.ts— NEW file to create. Parent dir src/routes/ exists (scaffold merged via PR #16). Valid SvelteKit server route path. - [x]
src/routes/api/webhooks/stripe/+server.ts— NEW file to create. Valid SvelteKit server route path for webhook handler. - [x]
src/lib/stripe.ts— NEW file to create. src/lib/ exists (contains db.ts, types.ts, schema.sql). Consistent placement.
All file targets are new files to be created. Existing scaffold (PR #16) and DB layer (PR #15) provide the foundation. No existing line references to verify. Targets are specific enough for an agent to act on without guessing.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, all file targets are in the same repo. Single-repo scope. No cross-repo concerns.
Dependencies
- [x] #8 SvelteKit scaffold — satisfied (merged as PR #16)
- [x] #9 Postgres schema + db.ts — satisfied (merged as PR #15)
- [x] #2 Stripe spike — satisfied (merged as PR #6, FINDINGS.md at spike/stripe/FINDINGS.md)
- [x] #10 Keycloak OIDC auth — parallel work, no dependency
All hard dependencies satisfied. Note:
stripenpm package is not yet in package.json — agent must runnpm install stripeas part of implementation.Acceptance Criteria
6 criteria, all verifiable by an agent:
- Three tier buttons create Checkout sessions — testable via unit test or API call to the endpoint
- Stripe Checkout (hosted) opens with correct amount — verifiable via session object's amount_total field
- Webhook receives checkout.session.completed — testable via
stripe listen --forward-to - Game + Payment records created in DB — verifiable via SQL query after test payment
- Challenger redirected to game status page — verifiable via Checkout session success_url config
- Webhook signature verified — verifiable via code inspection (constructEvent call) + failing test with bad signature
All criteria are concrete and testable. No ambiguity.
Blast Radius
Low. This is a new application with no downstream consumers yet. The webhook endpoint path (
/api/webhooks/stripe) is standard. No sibling services share this Stripe account (test keys are project-specific). The DB layer (db.ts) is already merged and this ticket consumes it without modifying it. Rollback is straightforward — revert the PR.Informational (does not block this ticket): The spike FINDINGS.md recommends manual payout (CashApp/Venmo) + full refund on challenger win, which diverges from the arch-dataflow diagram showing Stripe Connect Express for payouts. This discrepancy affects ticket #12 (Stripe Connect payout), not #11. Consider updating the arch diagram or revising #12 scope before that ticket reaches review.
Decomposition Assessment
3 file targets in 1 repo. 6 acceptance criteria. Estimated agent time: ~4-5 minutes. All three files are tightly coupled (stripe.ts is consumed by both route handlers). No independent subtasks that would benefit from parallelization. Fits within the three-thing limit (3 files) and five-minute rule. No decomposition needed.
Recommendation
No action needed. Ticket is ready for execution.
-
Review: Keycloak OIDC auth (Twitch login)
review-796-2026-04-03Verdict: READY
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Sub-ticket of #7, depends on #8 + #9
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- As a Twitch viewer / sign in with Twitch / Marcus knows gamertag
- [x] Context -- Keycloak OIDC via PKCE, realm twitch-2k-wager, client playme2k
- [x] File Targets -- 5 files listed
- [x] Acceptance Criteria -- 6 items
- [x] Test Expectations -- 2 items
- [x] Constraints -- PKCE, env vars, no raw OAuth
- [x] Checklist -- present
- [x] Related -- parent #7, story note, arch note
Traceability
- [x] story:challenger-auth label -- Challenger Twitch Login
- [x] story note verified -- story-twitch-2k-wager-challenger-auth (id 1157) exists in pal-e-docs
- [x] story entry on project page -- challenger-auth row present in project-twitch-2k-wager user-stories table
- [x] arch:dataflow-twitch-2k-wager label -- Data Flow: Twitch 2K Wager
- [x] arch note verified -- arch-dataflow-twitch-2k-wager (id 1155) exists in pal-e-docs
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#10, state: open
File Targets
- [x] src/routes/auth/login/+server.ts -- NEW file. Parent dir src/routes/ exists in repo. Correct SvelteKit server route pattern.
- [x] src/routes/auth/callback/+server.ts -- NEW file. Standard OIDC callback route.
- [x] src/routes/auth/logout/+server.ts -- NEW file. Session teardown route.
- [x] src/hooks.server.ts -- NEW file. SvelteKit server hooks for auth guard.
- [x] src/lib/auth.ts -- NEW file. Parent dir src/lib/ exists. Keycloak OIDC helpers.
All files are net-new, created on top of the scaffold from #8 (now merged). Directory structure confirmed in repo.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager. All file targets are within this repo. Single-repo change.
Dependencies
- [x] #8 (SvelteKit scaffold + Dockerfile) -- CLOSED. Prerequisite met.
- [x] #9 (Postgres schema + db.ts) -- CLOSED. Prerequisite met. Challenger record creation depends on DB schema.
- [x] #11 (Stripe Checkout + webhook) -- parallel, no conflict. Independent subsystem.
- [x] #7 (parent ticket) -- open, tracking overall progress. No blocker.
All dependencies satisfied. No blockers.
Acceptance Criteria
6 criteria, all verifiable by an agent:
- "Login with Twitch" button redirect -- testable via route handler response
- Challenger record creation -- testable via DB query after auth
- Session persistence -- testable via cookie/session check across navigation
- Logout clears session -- testable
- Repeat visitor recognition -- testable via second login
- "Works on mobile" -- partially testable (responsive layout). Full device test requires manual verification. Acceptable for this scope.
Test expectations reference Keycloak test realm -- agent will need running Keycloak. Acceptable given platform infrastructure is deployed.
Blast Radius
Checked existing Keycloak OIDC patterns across platform. pal-e-app uses keycloak-js (client-side SPA pattern). This ticket correctly uses server-side OIDC routes (adapter-node). Different pattern, no cross-contamination risk. No other SvelteKit apps use server-side Keycloak OIDC -- this is the first implementation of that pattern. No downstream consumers affected. Rollback is straightforward (revert single PR).
Decomposition Assessment
Apply the three-thing limit and five-minute rule:
- 5 file targets in 1 repo -- all part of a single auth flow (login, callback, logout, guard, helpers). Not 5 discrete changes; it is 1 cohesive feature across 5 files.
- 6 acceptance criteria -- slightly above 5 threshold, but all within the same concern (auth lifecycle).
- Estimated agent time: 3-5 minutes. Borderline but within bounds.
- No independent subtasks that benefit from parallelization -- callback depends on login, guard depends on both, helpers are shared.
No decomposition needed.
Recommendation
No action needed.
-
Review: SvelteKit scaffold + Dockerfile
review-794-2026-04-03Verdict: READY
Re-review (r3). Both prior issues resolved: arch label fixed on board item, project page user-stories section confirmed present (r2 false positive from empty TOC on inline-HTML page).
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Sub-ticket of #7
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- developer wants a working scaffold so subsequent tickets can add features
- [x] Context -- promote playground CSS, dark Twitch theme, no logic yet
- [x] File Targets -- 8 file groups listed (all new files)
- [x] Acceptance Criteria -- 6 testable criteria
- [x] Test Expectations -- visual comparison + docker build
- [x] Constraints -- no auth/Stripe/DB, match playground CSS exactly
- [x] Checklist -- PR/tests/no-unrelated
- [x] Related -- parent #7 + playground PR #13
All required template sections present and substantive.
Traceability
- [x] story:challenger-auth label -- Challenger Twitch Login
- [x] story note verified -- story-twitch-2k-wager-challenger-auth (id 1157) exists, active, note_type user-story
- [x] story entry on project page verified -- project-twitch-2k-wager user-stories table lists challenger-auth with link to story note (r2 false positive corrected; page uses inline HTML so TOC was empty, but content is present)
- [x] arch:deployment-twitch-2k-wager label -- confirmed on board item #794 (label fix applied since r2)
- [x] arch note verified -- arch-deployment-twitch-2k-wager (id 1156) exists, active, note_type architecture. Deployment diagram includes twitch-2k-wager-app component, Dockerfile, adapter-node -- matches this ticket's scope.
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#8, open
All three traceability legs satisfied. Both backing notes confirmed.
File Targets
- [x] package.json, svelte.config.js, vite.config.ts, tsconfig.json -- new files (SvelteKit init). Repo has README.md + spike/ only. Correct for greenfield scaffold.
- [x] src/app.css -- new file promoted from playground style.css
- [x] src/routes/+layout.svelte -- new file (app shell with dark theme)
- [x] src/routes/+page.svelte -- new file promoted from playground index.html
- [x] src/routes/game/[id]/+page.svelte -- new file promoted from playground game-status.html
- [x] Dockerfile -- new file (multi-stage Node.js build for adapter-node)
- [x] .dockerignore -- new file
All targets are new files in a greenfield repo. No existing code conflicts. Source design available from merged pal-e-playground PR #13. Reference implementation at westside-contracts (same SvelteKit adapter-node + Dockerfile pattern).
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, matches ### Repo field. Single-repo ticket. Kustomize overlay scoped to separate issue #3 (item #788).
Dependencies
- [x] Playground PR #13 (pal-e-playground) -- merged. Source CSS and HTML available.
- [x] Parent issue #7 (item #793) -- open. This is the first sub-ticket; all others (#9-#13) depend on this scaffold.
- [x] Service onboarding #1 (item #786, needs_approval) -- downstream, not blocking scaffold.
- [x] Kustomize overlay #3 (item #788, todo) -- downstream, not blocking scaffold.
- [x] Item #800 (Annotate playground #14) -- in next_up. Shares story:challenger-auth. Independent prep task, not a blocker.
No unresolved blocking dependencies.
Acceptance Criteria
- [x] "npm run dev starts on port 3000" -- testable: run command, check port
- [x] "Landing page matches playground design visually" -- testable: screenshot comparison (final visual approval deferred to human)
- [x] "Game status page renders (with placeholder data)" -- testable: navigate to /game/test, verify render
- [x] "docker build . succeeds" -- testable: exit code check
- [x] "No Tailwind -- pure CSS vars" -- testable: grep package.json and node_modules for tailwind
- [x] "Mobile-first (390px viewport)" -- testable: CSS inspection for min-width media queries
All 6 AC are specific and programmatically verifiable. No missing criteria.
Blast Radius
Minimal. Scaffolding a new project in a near-empty repo. No downstream consumers, no shared libraries, no auth/payment/DB integration. Same proven pattern as westside-contracts. Rollback is trivial (revert single PR on greenfield repo).
Decomposition Assessment
1 repo, 8 file targets (all new), 6 ACs. SvelteKit scaffold + CSS promotion + Dockerfile is one cohesive unit.
- Discrete changes: 3 (SvelteKit init, CSS/route promotion, Dockerfile). At the three-thing limit.
- Estimated agent time: 3-5 minutes. Within the five-minute rule.
- Parallelizable subtasks: None. SvelteKit init must precede route promotion.
No decomposition needed.
Recommendation
No action needed. Ticket is ready for execution.
-
Review: Annotate playground for SvelteKit promotion
review-800-2026-04-03Verdict: READY
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- bridges #4 (playground) to #8 (SvelteKit scaffold)
- [x] Repo -- forgejo_admin/pal-e-playground
- [x] User Story -- developer annotation for mechanical SvelteKit promotion
- [x] Context -- explains playground-to-production pipeline need
- [x] File Targets -- 2 files to modify (index.html, game-status.html), 1 exclusion (style.css)
- [x] Acceptance Criteria -- 8 items, all testable via grep
- [x] Test Expectations -- 2 items (render identity, developer readability)
- [x] Constraints -- 3 items (comments only, no CSS, must precede #8)
- [x] Checklist -- present (PR, tests, no unrelated changes)
- [x] Related -- 3 references (#4 source, #8 downstream, arch-dataflow note)
Traceability
- [x] story:challenger-auth label -- present on board item #800. Story note story-twitch-2k-wager-challenger-auth (id 1157) verified in pal-e-docs. Story key challenger-auth confirmed in project-twitch-2k-wager user-stories table.
- [x] arch:deployment-twitch-2k-wager label -- present on board item #800. Architecture note arch-deployment-twitch-2k-wager (id 1156) verified in pal-e-docs.
- [x] arch-dataflow-twitch-2k-wager referenced in AC #8 and Related section. Architecture note (id 1155) verified in pal-e-docs.
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#14, state=open
File Targets
- [x] twitch-2k-wager/index.html -- verified exists in pal-e-playground repo via Forgejo API
- [x] twitch-2k-wager/game-status.html -- verified exists in pal-e-playground repo via Forgejo API
- [x] style.css correctly excluded (exists in repo, not a target)
Targets are specific: 2 named HTML files in a known directory. Agent has no ambiguity about what to modify.
Repo Placement
OK. Forgejo issue filed on forgejo_admin/twitch-2k-wager (project tracker). Issue body ### Repo field correctly identifies forgejo_admin/pal-e-playground as the target repo. PR will be opened on pal-e-playground. Cross-repo tracking pattern -- acceptable and explicit. Single repo affected.
Dependencies
- [x] #4 (Landing page prototype, PR #13) -- satisfied. PR merged, files exist on main.
- [x] #8 (SvelteKit scaffold) -- downstream consumer, currently in backlog. Not a blocker; this ticket must complete first.
No unresolved dependencies.
Acceptance Criteria
8 ACs, all verifiable programmatically:
- AC 1-4: grep for COMPONENT, DATA, ROUTE, AUTH comment patterns in HTML files
- AC 5: grep for tier card annotations with dollar amounts
- AC 6: grep for state machine transition annotations in game-status.html
- AC 7: grep for Connect onboarding flow annotations
- AC 8: grep for arch-dataflow-twitch-2k-wager reference in comments
All specific and testable. No ambiguous language.
Blast Radius
Low. HTML comments only -- zero structural changes to markup or CSS. No existing COMPONENT/DATA/ROUTE/AUTH annotation convention elsewhere in the codebase. This introduces a new annotation standard for the playground-to-SvelteKit promotion pipeline. Contained to 2 files in 1 repo. Rollback is trivial (revert the comment additions).
Decomposition Assessment
2 file targets in 1 repo. 8 ACs but all are repetitive HTML comment annotations -- mechanical, uniform work. No discrete subtasks that benefit from parallelization. Single agent pass estimated under 5 minutes. No decomposition needed.
Recommendation
No action needed. Ticket is fully scoped, traceable, and ready for agent execution.
-
Review: Postgres schema + db.ts (re-review)
review-795-2026-04-03-v2Verdict: APPROVED
Re-review after previous false positives. All backing notes confirmed to exist. Scope is solid.
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Sub-ticket of #7, depends on #8
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- developer story for schema foundation
- [x] Context -- references arch-domain-twitch-2k-wager
- [x] File Targets -- 3 files (db.ts, schema.sql, types.ts)
- [x] Acceptance Criteria -- 8 items, all testable
- [x] Test Expectations -- schema apply + connection test
- [x] Constraints -- pg package, match arch exactly
- [x] Checklist -- standard 3-item
- [x] Related -- parent, dependency, arch note
Traceability
- [x] story:game-status label -- present on board item #795
- [x] story note verified -- story-twitch-2k-wager-game-status (id 1159) exists, listed in project-twitch-2k-wager user-stories table
- [x] arch:domain-twitch-2k-wager label -- present on board item #795
- [x] arch note verified -- arch-domain-twitch-2k-wager (id 1154) exists with full ER diagram
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#9, state: open
File Targets
- [x] src/lib/db.ts -- creation target. Repo currently has only README.md. Parent directory will exist after #8 (scaffold) creates src/ structure. Valid.
- [x] src/lib/schema.sql -- creation target. DDL for 5 tables matching arch-domain-twitch-2k-wager ER diagram.
- [x] src/lib/types.ts -- creation target. TypeScript types mirroring schema.
All targets are specific enough for an agent to act on without guessing. Schema source of truth is arch-domain-twitch-2k-wager.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager, all file targets are in the same repo. Single-repo ticket. No cross-repo scope.
Dependencies
- [x] #8 (SvelteKit scaffold + Dockerfile) -- pending (backlog). Sequential dependency documented in Lineage. Must complete before this ticket executes. Not a scope problem -- intentional ordering.
- [x] arch-domain-twitch-2k-wager -- satisfied. Note exists with full ER diagram that defines all 5 tables.
- [x] CNPG cluster -- satisfied. Service onboarding (#1) is closed/merged (PR #54 pal-e-services).
Acceptance Criteria
8 criteria, all agent-verifiable:
- Schema-to-arch matching: agent can diff created schema.sql against arch-domain-twitch-2k-wager ER diagram. Columns, types, FKs, enums all explicitly specified in both the AC and the arch note.
- Connection test: concrete SQL command (SELECT 1) with real env var (DATABASE_URL).
- All amounts in cents: verifiable by reading schema.sql column types (integer).
- No ambiguous language. Every criterion is testable.
Blast Radius
- New project, new schema. No existing consumers.
- CNPG cluster is shared infrastructure but schema is project-scoped (playme2k database).
- No sibling services share these tables.
- Rollback: DROP TABLE on all 5 tables. Straightforward since no data exists yet.
Decomposition Assessment
3 file targets in 1 repo. 8 AC but all are verification checkpoints for one coherent task (define 5-table schema + connection layer + TypeScript types). The three files are tightly coupled -- schema.sql defines tables, types.ts mirrors them, db.ts connects to them. Splitting would create artificial boundaries and require coordination overhead.
- Discrete changes: 3 (one per file). Within the three-thing limit.
- Estimated agent time: under 5 minutes. Schema is fully defined in the arch note -- the agent is transcribing, not designing.
- No independent subtasks that benefit from parallelization.
No decomposition needed.
Recommendation
No action needed. Ticket is ready for execution once #8 (scaffold) is complete.
-
Review: SvelteKit app: Keycloak + Stripe + game state
review-793-2026-04-03Verdict: 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)."
-
Review: Service onboarding: Keycloak + Twitch IdP + CNPG + funnel
review-786-2026-04-03-finalVerdict: APPROVED
Third review pass. All four items from previous NEEDS_REFINEMENT verdict have been resolved. Scope is tight, traceability complete, file targets verified.
Template Completeness
- [x] Type — Feature
- [x] Lineage — Standalone
- [x] Repo — forgejo_admin/pal-e-services
- [x] User Story — Twitch viewer sign-in via Keycloak
- [x] Context — thorough, includes key decisions (service key, CNPG strategy)
- [x] File Targets — 4 files to modify, 2 files explicitly excluded
- [x] Acceptance Criteria — 7 items, all verifiable
- [x] Test Expectations — 4 items with concrete commands
- [x] Constraints — 4 items including tofu plan -lock=false
- [x] Checklist — present
- [x] Related — project, arch notes, sibling ticket
Traceability
- [x] story:challenger-auth label — "Challenger Twitch Login"
- [x] story note verified — story-twitch-2k-wager-challenger-auth (id 1157) exists, listed in project-twitch-2k-wager user-stories table
- [x] arch:dataflow label — Data Flow architecture
- [x] arch note verified — arch-dataflow-twitch-2k-wager (id 1155) exists, active
- [x] arch:deployment label — Deployment architecture
- [x] arch note verified — arch-deployment-twitch-2k-wager (id 1156) exists, active
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#1, open
File Targets
- [x] k3s.tfvars — verified: contains keycloak_realms, keycloak_clients, and services maps to extend. Existing patterns (westside-basketball realm, westside-landing client, 8 service entries) provide clear templates.
- [x] keycloak.tf — verified: contains realm, client, role resources via for_each. No existing keycloak_oidc_identity_provider resource — confirms this is a NEW resource type as ticket states.
- [x] variables.tf — verified: contains keycloak_realms and keycloak_clients type definitions. No identity_provider variable type exists yet — needs adding as ticket states.
- [x] cnpg.tf — verified: shared pal-e-postgres cluster in postgres namespace. Comment explicitly documents that new databases require SQL, not manifest changes. Ticket correctly targets this file for database + role provisioning.
- [x] services.tf — correctly listed as "NOT to touch" — generic for_each over var.services handles everything.
Repo Placement
OK. Issue body correctly identifies pal-e-services as the target repo. Forgejo issue is filed on twitch-2k-wager (project repo), which is the correct organizational home. Single repo scope — no cross-repo concerns.
Dependencies
- [x] Board item #788 (Kustomize overlay, forgejo_admin/twitch-2k-wager#3) — depends on this ticket, documented in Related section. Currently in todo column.
- [x] No upstream blockers. This ticket is foundational infra with no prerequisites.
Acceptance Criteria
All 7 criteria are agent-verifiable:
- AC 1-3 (Keycloak realm, Twitch IdP, OIDC client): verifiable via tofu plan output showing expected resources
- AC 4 (service key playme2k): verifiable via tofu plan showing Harbor project, namespace, ArgoCD app, funnel
- AC 5 (database + role): verifiable via tofu plan or kubectl exec psql check
- AC 6 (Twitch credentials as variables): verifiable by checking variables.tf definitions
- AC 7 (clean tofu plan): directly executable command
Test expectations are concrete: tofu plan, kubectl get ns, Keycloak admin console, HTTPS resolution check.
Blast Radius
Low. Net-new resources only — no modifications to existing infrastructure. The keycloak_oidc_identity_provider is a new resource type not yet in the codebase, so no existing patterns to break. The services for_each is additive. CNPG database creation is additive to the shared cluster. Rollback is straightforward: tofu destroy targeting the new resources.
Decomposition Assessment
4 file targets, 1 repo, 7 AC. The AC count is slightly above the 5 threshold, but all resources are tightly coupled — a single tofu apply provisions the full stack (realm, IdP, client, service entry, database). Decomposing would create artificial boundaries between interdependent Terraform resources that must be applied together. No independent subtasks exist that could be parallelized. No decomposition needed.
Previous Review Resolution
- [x] Namespace vs hostname — RESOLVED. Service key = playme2k, namespace = playme2k, hostname = playme2k.tail5b443a.ts.net. Explicitly stated in issue body.
- [x] CNPG strategy — RESOLVED. Shared cluster, new database. Explicitly stated: "Use the existing pal-e-postgres CNPG cluster in the postgres namespace."
- [x] Phantom SOP reference — RESOLVED. Removed from issue body.
- [x] arch:twitch-oauth label — RESOLVED. Changed to arch:dataflow,arch:deployment. Both backing notes exist.
Recommendation
No action needed.
-
Review: Kustomize overlay (pal-e-deployments) [re-review]
review-788-2026-04-03-r2Verdict: READY
Re-review of board item #788 after complete issue rewrite. All 10 recommendations from
review-788-2026-04-03have been addressed.Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Related to #1, dependency documented
- [x] Repo --
forgejo_admin/pal-e-deployments - [x] User Story -- "As a Twitch viewer I want the playme2k website to be publicly accessible and deployed via GitOps"
- [x] Context -- explains relationship to #1 (pal-e-services creates namespace/ArgoCD app, this ticket creates the overlay)
- [x] File Targets -- correct paths, correct create/not-touch boundaries
- [x] Acceptance Criteria -- 6 testable criteria
- [x] Test Expectations -- 3 items including kustomize build validation
- [x] Constraints -- references basketball-api/prod as pattern, excludes CNPG/secrets, documents #1 dependency
- [x] Checklist -- present
- [x] Related -- project page, arch note, prerequisite issue
All required sections present and complete.
Traceability
- [x] story:challenger-auth label -- "Sign in with 1 click from stream link"
- [x] story note verified --
story-twitch-2k-wager-challenger-authexists in pal-e-docs and is listed in project-twitch-2k-wager user-stories table - [x] arch:deployment label -- present on board item
- [x] arch note verified --
arch-deployment-twitch-2k-wagerexists in pal-e-docs (id: 1156, note_type: architecture, status: active). Contains Mermaid deployment diagram showing kustomize overlay, ArgoCD, Tailscale funnel, and all components. - [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#3, open
Minor note: Board item label is
arch:deployment(generic) while the actual arch note slug isarch-deployment-twitch-2k-wager(project-specific). This is cosmetically imprecise but not blocking -- the issue body correctly references the full slug in the Related section.File Targets
- [x]
overlays/twitch-2k-wager/prod/kustomization.yaml-- to be created. Path follows established pattern (verified against 12 existing overlays: basketball-api, westsidekingsandqueens, mcd-tracker, etc.). Will reference../../../bases/standardwhich exists. - [x]
overlays/twitch-2k-wager/prod/deployment-patch.yaml-- to be created. Matches the patch pattern used by basketball-api and westsidekingsandqueens overlays. Env vars listed (DATABASE_URL, STRIPE keys, Keycloak config) are appropriate for the app. - [x]
bases/standard/correctly excluded -- shared base contains deployment.yaml, service.yaml, hpa.yaml, networkpolicy.yaml, servicemonitor.yaml, kustomization.yaml - [x] Other overlay directories correctly excluded
- [x] pal-e-services files correctly excluded (separate ticket #1)
All file targets verified against the actual pal-e-deployments repo structure. Paths are correct and specific enough for an agent.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager (project repo), work targets forgejo_admin/pal-e-deployments. This matches the established pattern where project repos hold issues and pal-e-deployments holds the kustomize overlays. Single-repo scope is clean.
Dependencies
- [x] Ticket #1 (pal-e-services onboarding) -- DOCUMENTED in Lineage and Constraints. Board item #786 exists in backlog. Sequential dependency is clear: namespace + ArgoCD app must exist before overlay can sync.
- [x] App image -- acknowledged in Test Expectations: "Pod starts (may CrashLoop until app image exists -- that's expected)." Appropriate for infra-first approach.
- [x] bases/standard -- exists in pal-e-deployments, verified.
No unresolved or undocumented dependencies.
Acceptance Criteria
6 criteria, all verifiable by an agent after implementation:
- [x] "kustomization.yaml inherits from bases/standard" -- verifiable by reading the file
- [x] "Namespace set to twitch-2k-wager" -- verifiable in kustomization.yaml
- [x] "Deployment patch includes env vars" -- verifiable by reading deployment-patch.yaml
- [x] "Image reference points to Harbor" -- verifiable in kustomization.yaml images section
- [x] "Port set to 3000" -- verifiable in deployment-patch.yaml
- [x] "ArgoCD syncs successfully after merge" -- verifiable post-merge via argocd CLI; depends on #1 completing first (documented)
5 of 6 are file-content verifiable. AC #6 (ArgoCD sync) requires #1 to be completed and applied first, which is correctly documented as a dependency. No ambiguous language.
Blast Radius
Low. New overlay directory creation only. No modifications to existing overlays or shared bases. 12 existing overlays are unaffected. Standard pattern -- same structure used by all services. Rollback = delete the overlay directory.
Decomposition Assessment
No decomposition needed.
- 2 files to create in 1 repo -- well within 3-file limit
- 6 AC (5 file-verifiable, 1 post-deploy) -- borderline but all related to the same 2 files
- Estimated agent work: 3-4 minutes (copy pattern from basketball-api or westsidekingsandqueens, adapt names/env vars)
- No independent subtasks that could be parallelized -- both files are part of one coherent overlay
- Well within 5-minute rule
Recommendation
No action needed. Ticket is ready for dispatch.
Optional improvement (not blocking):
- [LABEL] Refine
arch:deploymenttoarch:deployment-twitch-2k-wagerfor precise traceability to the arch note. Not required -- issue body already references the correct slug.
Previous Review Resolution
All 10 recommendations from
review-788-2026-04-03(NEEDS_REFINEMENT):- [BODY] Wrong file paths -- FIXED. Now correctly uses
overlays/twitch-2k-wager/prod/prefix. - [BODY] Remove namespace.yaml -- FIXED. Not mentioned in file targets.
- [BODY] Move CNPG scope -- FIXED. Constraints explicitly state "Do NOT create CNPG or secrets here."
- [BODY] Add pal-e-services file targets -- FIXED. Scope limited to pal-e-deployments only. pal-e-services is ticket #1.
- [BODY] Clarify funnel strategy -- RESOLVED. Funnel is in pal-e-services scope (#1).
- [BODY] Clarify secrets strategy -- RESOLVED. Constraints exclude secrets from this ticket.
- [BODY] Narrow AC -- FIXED. AC now focuses on overlay deliverables only.
- [LABEL] arch label mismatch -- PARTIALLY FIXED. Changed from
arch:tailscale-funneltoarch:deployment. Could be more precise but not blocking. - [SCOPE] Create arch-deployment-twitch-2k-wager -- FIXED. Note exists (id: 1156).
- [SCOPE] Create service-onboarding-sop -- OUT OF SCOPE. Not referenced in rewritten issue.
-
Review: Monitoring: golden signals + blackbox probe
review-792-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — references #1 and #3 as prerequisites
- [x] Repo — forgejo_admin/pal-e-platform
- [x] User Story — present, well-formed (platform operator wants golden signals + probes)
- [x] Context — clear motivation (stream uptime = revenue + trust)
- [x] File Targets — 3 files listed with clear intent
- [x] Acceptance Criteria — 4 criteria
- [x] Test Expectations — 3 expectations including tofu plan -lock=false
- [x] Constraints — present (follow existing patterns, tofu plan -lock=false, dependency ordering)
- [x] Checklist — present
- [x] Related — present with cross-references to project page, arch note, and prerequisite issues
Traceability
- [x] story:observability label — "Platform Observability"
- [x] story note verified — story-twitch-2k-wager-observability exists in pal-e-docs (id: 1166)
- [ ] story NOT listed on project page — [SCOPE] The observability story is missing from the project-twitch-2k-wager user-stories table. Only challenger-auth, challenger-pay, game-status, and winner-payout are listed. Add observability row to the table.
- [x] arch:monitoring label present
- [ ] arch note MISSING — [SCOPE] No arch-monitoring note exists in pal-e-docs. This work uses pal-e-platform's existing monitoring module, not a new twitch-2k-wager-specific component. Recommend either: (a) create arch-monitoring note describing the platform monitoring module, or (b) change label to arch:deployment since arch-deployment-twitch-2k-wager already exists and covers the deployment topology this monitoring extends.
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#5, state: open
File Targets
- [x] terraform/modules/monitoring/main.tf — verified: file exists (844 lines). Blackbox exporter targets list at lines 342-422. New probe entry follows existing pattern (name/url/labels object). Dashboard ConfigMap pattern at lines 646-660 shows how to wire a new JSON file.
- [x] terraform/dashboards/ — verified: directory exists with 4 JSON files. pal-e-docs-golden-signals.json is the template to copy. New file twitch-2k-wager-golden-signals.json would be created here.
- [ ] terraform/modules/ops/main.tf — ISSUE: ticket says "add service to ops module if applicable." File exists (294 lines) but contains NVIDIA device plugin, Ollama, embedding worker metrics, and TF state backup CronJob — none related to service monitoring. This target is not needed. [BODY] Remove or replace the ops/main.tf file target. All monitoring work belongs in monitoring/main.tf and dashboards/.
Repo Placement
Issue filed on forgejo_admin/twitch-2k-wager but all work targets forgejo_admin/pal-e-platform. The issue body correctly declares Repo: forgejo_admin/pal-e-platform. Cross-repo pattern is acceptable — project issues live on the project repo, implementation may touch platform. Single target repo (pal-e-platform), so no additional Forgejo issues needed.
Dependencies
- [ ] twitch-2k-wager#1 (service onboarding) — pending (backlog). Required for service to exist in cluster.
- [ ] twitch-2k-wager#3 (kustomize overlay) — pending (backlog). Required for ArgoCD deployment.
Both prerequisites are in backlog. The blackbox probe URL (https://playme2k.tail5b443a.ts.net/health) requires the service to be deployed. tofu plan will succeed but the probe will fire EndpointDown alerts until dependencies are satisfied. This ordering concern is documented in the ticket Constraints section — acceptable.
Acceptance Criteria
4 AC, assessment:
- [x] "Blackbox probe configured" — testable via tofu plan output showing new target in blackbox_exporter helm values.
- [x] "Grafana dashboard shows golden signals" — testable: agent can verify JSON file has latency/traffic/errors/saturation panels.
- [ ] "Probe alerts on downtime (5xx or timeout)" — ambiguous. The existing EndpointDown PrometheusRule (line 428-476) already fires on probe_success == 0 for ALL blackbox targets. Agent does NOT need to create a new alert rule. [BODY] Rephrase to: "Existing EndpointDown alert covers the new probe target (no new alert rule needed)." This prevents the agent from creating a redundant PrometheusRule.
- [x] "Dashboard accessible in Grafana" — manual post-apply verification, reasonable.
Blast Radius
Low risk. All changes are additive:
- New entry in blackbox exporter targets list (no modification to existing probes)
- New dashboard JSON file (no modification to existing dashboards)
- New ConfigMap resource in monitoring/main.tf
Rollback: remove the three additions and tofu apply.
Note on probe URL pattern: the ticket specifies the external Tailscale funnel URL (https://playme2k.tail5b443a.ts.net/health) whereas all existing application probes use internal cluster URLs (e.g., http://pal-e-docs.pal-e-docs.svc.cluster.local:8000/healthz). Internal URLs are faster and avoid funnel round-trips. [BODY] Consider changing to internal URL: http://twitch-2k-wager-app.twitch-2k-wager.svc.cluster.local:3000/health — or document why external URL is intentionally preferred (e.g., testing the full funnel path).
Decomposition Assessment
Apply three-thing limit and five-minute rule:
- Discrete changes: 3 (add probe target, create dashboard JSON, add ConfigMap resource). At the limit but tightly coupled — all part of one monitoring setup.
- Repos: 1 (pal-e-platform)
- AC count: 4
- Estimated agent time: under 5 minutes
- Independent subtasks: none — dashboard and probe are logically coupled
No decomposition needed.
Recommendation
- [SCOPE] Add observability story row to project-twitch-2k-wager user-stories table (story note exists but is not listed on the project page).
- [SCOPE] Either create arch-monitoring note for the platform monitoring module, or change board item label from arch:monitoring to arch:deployment (which has a backing note).
- [BODY] Remove terraform/modules/ops/main.tf from file targets — ops module is unrelated to monitoring work.
- [BODY] Clarify AC "Probe alerts on downtime" — existing EndpointDown alert already covers all blackbox targets. Rephrase to prevent agent from creating redundant PrometheusRule.
- [BODY] Consider changing probe URL from external funnel URL to internal cluster URL to match existing app probe patterns, or document why external is preferred.
-
Review: Service onboarding: Keycloak + Twitch IdP + CNPG + funnel
review-786-2026-04-03-rescopeVerdict: NEEDS_REFINEMENT
Re-review after major re-scope from Twitch OAuth spike to full service onboarding feature. Issue body is dramatically improved -- well-structured, complete template, clear file targets. Three issues remain before READY.
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Standalone, replaces original spike scope
- [x] Repo -- forgejo_admin/pal-e-services
- [x] User Story -- Twitch viewer sign-in via Keycloak
- [x] Context -- Clear motivation, Twitch OAuth app policy noted
- [x] File Targets -- 4 files to modify, 2 files NOT to touch
- [x] Acceptance Criteria -- 8 criteria, specific and testable
- [x] Test Expectations -- 4 test commands, all concrete
- [x] Constraints -- References service-onboarding-sop, tofu -lock=false, credential source
- [x] Checklist -- Standard PR/test/no-unrelated
- [x] Related -- project, arch notes, SOP, sibling issue
Traceability
- [x] story:challenger-auth label -- Challenger Twitch Login
- [x] story note verified -- story-twitch-2k-wager-challenger-auth exists, listed in project-twitch-2k-wager user-stories table
- [x] arch:deployment label -- arch-deployment-twitch-2k-wager exists
- [ ] arch:twitch-oauth label -- NO MATCHING NOTE. No arch-twitch-oauth note exists. The OAuth flow is documented in arch-dataflow-twitch-2k-wager. [LABEL] Rename label to arch:dataflow or create arch-twitch-oauth as a redirect.
- [x] arch-domain-twitch-2k-wager -- exists (domain model with Mermaid ER diagram)
- [x] arch-dataflow-twitch-2k-wager -- exists (OAuth + payment sequence diagram)
- [x] arch-deployment-twitch-2k-wager -- exists (deployment topology diagram)
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#1, open
File Targets
- [x] terraform/k3s.tfvars -- verified: exists, contains keycloak_realms (2 realms), keycloak_clients (5 clients), services (9 entries) maps. Agent adds to each.
- [x] terraform/keycloak.tf -- verified: exists, has keycloak_realm and keycloak_openid_client for_each resources. Confirmed keycloak_oidc_identity_provider does NOT exist yet (new resource type as stated).
- [x] terraform/variables.tf -- verified: exists, has keycloak_realms and keycloak_clients variable types. No identity_provider variable type yet (new addition as stated).
- [ ] terraform/cnpg.tf -- ISSUE: exists but current pattern is a single shared CNPG cluster named pal-e-postgres in the postgres namespace. Issue says "add CNPG cluster for twitch-2k-wager namespace" which is a new per-namespace pattern. [BODY] Clarify: is this a second independent CNPG cluster, or a new database on the existing shared cluster? If new cluster, document as intentional pattern change.
- [x] terraform/services.tf -- correctly identified as NOT to touch (generic for_each on var.services).
Repo Placement
Correct. Issue filed on forgejo_admin/twitch-2k-wager (consumer repo for tracking) with work targeting forgejo_admin/pal-e-services (the terraform repo). Standard pattern -- onboarding issues track on the consumer, code changes on pal-e-services. Single repo for code changes.
Dependencies
- [x] Board item #788 (Kustomize overlay in pal-e-deployments) depends on this ticket -- documented in issue Related section as forgejo_admin/twitch-2k-wager#3.
- [x] No blockers on this ticket -- it can proceed independently.
- [x] Twitch Developer Console app registration is a manual prerequisite -- documented in Constraints (redirect URI specified).
- [ ] service-onboarding-sop -- referenced in Constraints but does not exist in pal-e-docs. [BODY] Remove reference or create the SOP first.
Acceptance Criteria
8 criteria, mostly testable. Issues found:
- [ ] AC5 + AC6 conflict: AC5 says funnel hostname = "playme2k". AC6 says CNPG in "twitch-2k-wager" namespace. But services.tf derives BOTH namespace (line 85: name = each.key) and funnel hostname (line 201: hosts = [each.key]) from the service map key. Cannot satisfy both without either (a) accepting namespace = playme2k, (b) extending var.services with a hostname_override field, or (c) managing the funnel outside var.services. [BODY] Resolve this conflict explicitly in the issue body.
- [x] AC1-4 -- Keycloak realm, Twitch IdP, OIDC client, service entry -- all testable via tofu plan output inspection.
- [x] AC7 -- Twitch credentials as Terraform vars -- straightforward.
- [x] AC8 -- Clean tofu plan -- standard gate.
- [x] Test Expectations -- all 4 commands are concrete and runnable.
Blast Radius
Low. All changes are additive -- new realm, new client, new service entry, new IdP resource type. No modifications to existing resources. The new keycloak_oidc_identity_provider resource is isolated to the new realm. Risk: if identity_provider variables modify the existing keycloak_realms type signature it could break existing realms. Agent should use a separate variable block for identity providers, not modify the realm type. Rollback is straightforward (tofu destroy targeted resources).
Decomposition Assessment
4 file targets in 1 repo, 8 AC. All targets are tightly coupled Terraform resources provisioned in a single tofu apply. The Keycloak realm, client, IdP, service entry, and CNPG cluster are interdependent -- splitting would create artificial boundaries and require multiple apply cycles. A single agent pass is appropriate. Estimated under 5 minutes. No decomposition needed.
Recommendation
- [BODY] Resolve namespace vs hostname conflict. services.tf derives both namespace and funnel hostname from the service map key. State which approach: (a) service key = playme2k, accept namespace = playme2k; (b) extend var.services with a hostname field; or (c) manage funnel separately outside var.services.
- [BODY] Clarify CNPG strategy: new per-namespace cluster or new database on the shared pal-e-postgres cluster in postgres namespace. Current cnpg.tf has one cluster. A second cluster is a pattern change that should be documented as intentional.
- [BODY] Remove or replace reference to service-onboarding-sop in Constraints -- this note does not exist in pal-e-docs.
- [LABEL] Rename arch:twitch-oauth label to arch:dataflow (matching arch-dataflow-twitch-2k-wager) or create an arch-twitch-oauth redirect note.
-
Review: Service onboarding: playme2k funnel + kustomize
review-788-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Standalone
- [x] Repo -- forgejo_admin/pal-e-deployments
- [x] User Story -- clear "As a Twitch viewer I want the site publicly accessible"
- [x] Context -- sufficient background on service onboarding pattern
- [x] File Targets -- present but inaccurate (see below)
- [x] Acceptance Criteria -- 5 testable criteria
- [x] Test Expectations -- curl health check + Stripe webhook test
- [x] Constraints -- references service-onboarding-sop, CNPG, Tailscale
- [x] Checklist -- present
- [x] Related -- references project page, arch note, SOP
Traceability
- [x] story:challenger-auth label -- "Sign in with 1 click from stream link"
- [x] story note verified -- challenger-auth listed in project-twitch-2k-wager user-stories table
- [x] arch:tailscale-funnel label -- present on board item
- [ ] arch note MISSING -- [SCOPE] No
arch-tailscale-funnelnote exists in pal-e-docs. The issue's Related section referencesarch-deployment-twitch-2k-wagerwhich also does not exist. The arch label and the referenced note name are mismatched. Recommend creatingarch-deployment-twitch-2k-wagerand updating board item label toarch:deployment-twitch-2k-wager. - [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#3, open
File Targets
- [ ]
twitch-2k-wager/kustomization.yaml-- ISSUE: Wrong path. Established pattern isoverlays/twitch-2k-wager/prod/kustomization.yaml. All 10+ existing services useoverlays/{service}/prod/with a base reference to../../../bases/standard. - [ ]
twitch-2k-wager/deployment.yaml-- ISSUE: Standard pattern does NOT use standalone deployment.yaml. Base template (bases/standard/deployment.yaml) is inherited via kustomize. Overlays usedeployment-patch.yamlfor container name, port, and env overrides. - [ ]
twitch-2k-wager/service.yaml-- ISSUE: Same as above. Base service is inherited. Overlay patches rename and set port via JSON patches in kustomization.yaml. - [ ]
twitch-2k-wager/namespace.yaml-- ISSUE: Namespaces are created by pal-e-services terraform (kubernetes_namespace_v1.service), not by kustomize overlays. No existing overlay contains a namespace.yaml. - [ ]
twitch-2k-wager/cnpg-cluster.yaml-- ISSUE: CNPG clusters are managed in pal-e-services terraform (cnpg.tf), not in pal-e-deployments overlays. No existing overlay contains a CNPG resource. - [ ]
twitch-2k-wager/secrets.yaml-- ISSUE: Harbor image pull secrets are managed by pal-e-services terraform. Application secrets (Stripe/Twitch) need SOPS-encrypted yaml (likeharbor-creds.enc.yamlin westside overlay), but the issue doesn't mention SOPS. - [ ] Tailscale funnel config -- ISSUE: Ambiguous. Funnels are either terraform-managed (
funnel = truein pal-e-services) or overlay-managed viaingress.yaml(funnel = false). The desired hostnameplayme2kdiffers from the likely service keytwitch-2k-wager. Needs explicit decision.
Assessed: file targets are NOT specific enough for an agent. Every listed path is wrong or belongs in a different repo.
Repo Placement
ISSUE: Ticket targets only
forgejo_admin/pal-e-deployments, but the actual service onboarding pattern requires changes in TWO repos:- pal-e-services (terraform/k3s.tfvars) -- Add service entry to
servicesmap. This creates Harbor project, robot accounts, namespace, ArgoCD application, and optionally the Tailscale funnel. Potentially add CNPG cluster tocnpg.tf. - pal-e-deployments (overlays/twitch-2k-wager/prod/) -- Kustomize overlay: kustomization.yaml, deployment-patch.yaml, ingress.yaml (if funnel=false), harbor-creds.enc.yaml.
This is a multi-repo ticket. The pal-e-services changes must be applied (
tofu apply) before ArgoCD can sync the pal-e-deployments overlay.Dependencies
- [x] CNPG operator -- satisfied (already deployed per Constraints section)
- [ ] pal-e-services tofu apply -- PENDING: service entry must be created and applied before ArgoCD can sync. Not documented in the ticket.
- [ ] Harbor project -- PENDING: created by pal-e-services tofu apply. Required before first image push.
- [ ] App image -- PENDING: no app code exists yet (repo is empty). The overlay can be created but ArgoCD sync will fail until an image is pushed.
- [ ] Stripe/Twitch credentials -- PENDING: no secrets exist yet. AC #4 ("secrets mounted") cannot pass until credentials are provisioned.
Multiple unresolved dependencies not documented in the issue scope.
Acceptance Criteria
- "playme2k.tail5b443a.ts.net resolves and serves HTTPS" -- testable via curl, but depends on app image existing
- "ArgoCD syncs the deployment" -- verifiable via argocd CLI, but sync will fail without an image
- "CNPG Postgres cluster running" -- verifiable via kubectl, but unclear if CNPG is in this ticket's scope or separate
- "Stripe and Twitch secrets mounted" -- UNCLEAR: does this ticket create the secrets or just the mount points?
- "Webhook endpoint reachable" -- depends on app being deployed with working routes
Several AC depend on work outside this ticket's stated scope (app code, credentials). An agent cannot verify these criteria after implementation of just the infra.
Blast Radius
Low risk. New overlay and new service entry -- no changes to existing services. Standard pattern established across 10+ services. Only concern: hostname decision (
playme2kvstwitch-2k-wager) affects DNS and TLS. Rollback is straightforward (remove service entry + overlay).Decomposition Assessment
Apply three-thing limit and five-minute rule:
- Touches 2 repos (pal-e-services + pal-e-deployments) with sequential dependency (tofu apply before ArgoCD sync)
- 5 acceptance criteria, several untestable without app code
- File targets span infrastructure (terraform) and deployment (kustomize) -- distinct concerns
- Estimated agent work: borderline. The pal-e-services tfvars addition is ~10 lines and the overlay is a template copy. A single agent COULD do both sequentially in ~5 minutes IF the ticket is correctly scoped with accurate file targets.
Recommendation: Fix file targets and repo placement first. After refinement, reassess whether decomposition is needed. The sequential tofu-apply dependency suggests two tickets may be cleaner, but it's not strictly required.
Recommendation
- [BODY] Fix all file paths from
twitch-2k-wager/Xtooverlays/twitch-2k-wager/prod/X. Replace standalone deployment.yaml, service.yaml, namespace.yaml with the actual kustomize pattern:kustomization.yaml,deployment-patch.yaml,ingress.yaml,harbor-creds.enc.yaml. - [BODY] Remove
namespace.yamlfrom file targets -- namespaces are created by pal-e-services terraform. - [BODY] Move CNPG scope to pal-e-services file targets (
terraform/cnpg.tf) or split to a separate ticket. - [BODY] Add pal-e-services file targets:
terraform/k3s.tfvars(service entry) and optionallyterraform/cnpg.tf. - [BODY] Clarify funnel strategy: terraform-managed (
funnel = true, service key =playme2k) vs overlay-managed (funnel = false, custom ingress.yaml with TLS hostplayme2k). Document the hostname decision. - [BODY] Clarify secrets strategy: SOPS-encrypted yaml or deferred to a separate ticket?
- [BODY] Narrow AC to what this infra ticket can actually verify (overlay exists, ArgoCD app created, namespace exists) vs what requires app code.
- [LABEL] Change
arch:tailscale-funneltoarch:deployment-twitch-2k-wagerto match the referenced architecture component. - [SCOPE] Create architecture note
arch-deployment-twitch-2k-wagerfor the deployment component. - [SCOPE] Create
service-onboarding-sopnote in pal-e-docs (referenced in Constraints but does not exist).
-
Review: Spike: Stripe payment + payout + revenue split
review-787-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
Checked against
template-issue-spikerequired sections:- [x] Type — Spike
- [x] Lineage — Standalone, second ticket for project
- [x] Repo —
forgejo_admin/twitch-2k-wager - [x] Question — Well-framed "which approach" question covering buy-in collection, payout mechanics, and revenue split
- [x] What to Explore — Detailed with five investigation areas (buy-in, payout options, split tracking, webhook handler, test mode)
- [x] Success Criteria — 5 criteria, all checkboxed
- [x] Time-box — 2 hours with clear reasoning ("payment collection is straightforward, payout mechanics are the unknown")
- [x] Related — 4 items listed (project page, board, two user stories)
All spike template sections present and well-populated. Template is complete.
Traceability
- [x] story:challenger-pay label — present on board item
- [ ] story note MISSING — No note found for story:challenger-pay. Issue Related section self-documents this: "story-twitch-2k-wager-challenger-pay (to be created)". [SCOPE] Create user story entry on project-twitch-2k-wager user-stories section.
- [x] story:winner-payout label — present on board item
- [ ] story note MISSING — No note found for story:winner-payout. Issue Related section self-documents this: "story-twitch-2k-wager-winner-payout (to be created)". [SCOPE] Create user story entry on project-twitch-2k-wager user-stories section.
- [x] arch:stripe-api label — present on board item
- [ ] arch note MISSING — No arch-stripe-api note found in pal-e-docs. [SCOPE] Create architecture note arch-stripe-api for the Stripe integration component.
- [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/forgejo_admin/twitch-2k-wager/issues/2, state: open
- [ ] project-twitch-2k-wager — Project page not found in pal-e-docs (empty TOC, no search results). All traceability anchors depend on this page existing. [SCOPE] Create project page project-twitch-2k-wager with user-stories and architecture sections.
File Targets
N/A — Spike type. No file targets expected. Issue mentions
~/secrets/stripe/as credential location, which is informational context for the spike investigator, not a code target.Repo Placement
OK. Issue is filed on
forgejo_admin/twitch-2k-wager. Repo exists and is confirmed via Forgejo API (private repo, SvelteKit + Stripe per description). The spike investigates Stripe architecture for this project — correct placement.Dependencies
- Board item #786 (Spike: Twitch OAuth for public users) — same board, backlog. Independent spike — OAuth and payments are orthogonal concerns. No blocking dependency.
- Board item #788 (Service onboarding: playme2k funnel + kustomize) — overlapping concern: the "Where does the webhook handler live?" question in this spike touches the same service architecture that #788 will configure. Not a hard blocker, but the spike investigator should be aware.
- Board item #789 (Landing page HTML/CSS prototype) — depends on both spikes completing first to inform UI flows. No blocking concern for this spike.
- No dependencies are documented in the issue body. The Related section lists project and board but not sibling tickets.
Acceptance Criteria
5 criteria assessed for testability:
- [x] "Payout mechanism decided with evidence" — verifiable: spike output is a decision doc with evidence. Agent can confirm doc exists.
- [x] "Revenue split tracking approach decided" — verifiable: spike output is a decision doc. Agent can confirm approach is documented.
- [x] "Working Stripe Checkout test flow" — verifiable: agent can run the test flow locally with test keys and confirm session creation + webhook receipt.
- [x] "Webhook delivery confirmed" — verifiable: Stripe CLI output or test endpoint logs. Issue specifically mentions verification method.
- [x] "Follow-up feature tickets created" — verifiable: agent can check Forgejo issues list after spike completion.
All criteria are testable. Good specificity on verification methods (Stripe CLI explicitly mentioned).
Blast Radius
Greenfield spike for a new project — no existing codebase to affect. No sibling services share Stripe credentials yet. The revenue split decision may set a precedent for future Marcus/Lucas ventures, but that is a business decision, not a code blast radius. Low risk. Rollback is N/A (spike produces decisions, not deployments).
Decomposition Assessment
Apply three-thing limit and five-minute rule:
- 5 acceptance criteria, but this is a spike (investigation, not implementation) — criteria are research questions, not code changes
- 1 repo scope
- Time-boxed to 2 hours by design
- No discrete independent subtasks that need parallelization — the investigation is sequential (understand options, test one flow, decide)
No decomposition needed. Spike is appropriately scoped as a single investigation pass.
Recommendation
- [SCOPE] Create project page
project-twitch-2k-wagerwith user-stories and architecture sections. This is the anchor for all traceability — both story labels and the arch label reference backing notes that do not yet exist. - [SCOPE] Create user story entries for
story:challenger-payandstory:winner-payouton the project page user-stories section. - [SCOPE] Create architecture note
arch-stripe-apifor the Stripe integration component. - [BODY] Add sibling ticket awareness to Related section: "Board item #788 (Service onboarding) may affect webhook handler placement decision."
-
Review: Landing page HTML/CSS prototype
review-789-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Standalone, first UI work
- [x] Repo -- forgejo_admin/pal-e-playground
- [x] User Story -- clear who/what/why
- [x] Context -- playground-first pipeline, landing page purpose explained
- [x] File Targets -- 3 files to create (index.html, style.css, game-status.html)
- [x] Acceptance Criteria -- 8 testable items
- [x] Test Expectations -- visual review at 390px + Lucas approval
- [x] Constraints -- pure HTML/CSS, no Tailwind, dark theme, mobile-first
- [x] Checklist -- PR opened, tests pass, no unrelated changes
- [x] Related -- project page, story notes, game-status story
Traceability
- [x] story:challenger-auth label -- present on board item, listed in project-twitch-2k-wager user-stories table
- [ ] story note story-twitch-2k-wager-challenger-auth MISSING -- [SCOPE] Create story note story-twitch-2k-wager-challenger-auth in pal-e-docs
- [x] story:challenger-pay label -- present on board item, listed in project-twitch-2k-wager user-stories table
- [ ] story note story-twitch-2k-wager-challenger-pay MISSING -- [SCOPE] Create story note story-twitch-2k-wager-challenger-pay in pal-e-docs
- [x] arch:twitch-2k-wager-app label -- present on board item
- [ ] arch note MISSING -- [SCOPE] Create architecture note arch-twitch-2k-wager-app in pal-e-docs
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#4, open
File Targets
- [x] twitch-2k-wager/index.html -- new file in pal-e-playground; parent repo exists, directory will be created
- [x] twitch-2k-wager/style.css -- new file in pal-e-playground; same directory
- [x] twitch-2k-wager/game-status.html -- new file in pal-e-playground; same directory
Targets are specific enough for an agent to act on without guessing.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager (project repo). Work targets forgejo_admin/pal-e-playground. Correct per playground-first pipeline convention -- prototypes live in pal-e-playground, Forgejo issue lives on the project repo. Single repo scope, no multi-repo concerns.
Dependencies
- [x] No blocking dependencies -- this is pure HTML/CSS with no backend requirements
- [x] Board items #786 (Twitch OAuth spike), #787 (Stripe spike), #788 (service onboarding) are all in backlog but none block this prototype work
No unresolved dependencies.
Acceptance Criteria
8 criteria evaluated:
- [x] "Landing page communicates wager concept in under 5 seconds" -- subjective but appropriate for visual prototype; Lucas approval is the gate
- [x] "Three buy-in tiers with pot amounts" -- specific and verifiable in markup
- [x] "Login with Twitch button prominent" -- verifiable in DOM
- [x] "Payout timeline clearly stated" -- verifiable in content
- [x] "Win/lose outcomes explained" -- verifiable in content
- [x] "Mobile-first 390px viewport" -- verifiable via browser devtools
- [x] "Dark theme matches Twitch vibe" -- subjective, Lucas approval gate
- [x] "Game status page shows payment/progress/result" -- verifiable in game-status.html
All criteria are appropriate for a playground prototype. Visual/subjective items are gated by Lucas approval in Test Expectations.
Blast Radius
Minimal. New directory in pal-e-playground with 3 new files. No existing code is modified. No downstream consumers yet. No rollback concerns -- deleting the directory reverses everything.
Decomposition Assessment
Three-thing limit: 3 files in 1 repo, all in the same directory. Within limit.
Five-minute rule: Pure HTML/CSS prototype with clear specs. Well within 5 minutes for an agent.
Parallelization: Not applicable -- all 3 files are tightly coupled (shared styles, same design language).
No decomposition needed.
Recommendation
- [SCOPE] Create story note
story-twitch-2k-wager-challenger-authin pal-e-docs for the challenger-auth user story - [SCOPE] Create story note
story-twitch-2k-wager-challenger-payin pal-e-docs for the challenger-pay user story - [SCOPE] Create architecture note
arch-twitch-2k-wager-appin pal-e-docs for the app component
Once the three backing notes are created, this ticket is READY for execution. The issue body, labels, and repo placement are all correct.
User Story 6
-
Marcus Operator Flow
story-twitch-2k-wager-operator-flowstory: Marcus Operator Flow
Role
Marcus (streamer/operator)
Key
operator-flow
Want
As Marcus, I want to send someone a link, see when they've paid, know their gamertag, play them, and declare the winner
So That
So that I can run wager matches from my stream without needing Lucas or any technical knowledge
Acceptance Criteria
- [ ] Marcus copies
playme2k.tail5b443a.ts.net/playand texts/DMs it to a challenger - [ ] Challenger clicks link, picks tier, enters gamertag (PSN or 2K ID), pays via Stripe
- [ ] Marcus opens
/admin?key=SECRETon his phone and sees the challenger in the queue: gamertag, tier, pot amount, "paid" status - [ ] Marcus invites the challenger in NBA 2K using the gamertag shown in the queue
- [ ] They play on stream
- [ ] Marcus taps "Challenger Wins" or "Marcus Wins" on the admin page
- [ ] If challenger wins: payout initiated, challenger gets email receipt
- [ ] If Marcus wins: revenue split recorded (50/50 Marcus/Lucas), challenger gets "thanks for playing" email
- [ ] Marcus can see completed games and their results
- [ ] Entire flow works on Marcus's phone
Success Metric
Marcus can run a wager match from link-send to result-declaration in under 2 minutes of admin time. Zero calls to Lucas needed.
Related Architecture
- arch-dataflow-twitch-2k-wager — full flow sequence
- arch-domain-twitch-2k-wager — Game, RevenueSplit entities
Related
- project-twitch-2k-wager — parent project page
- board-twitch-2k-wager — project board
- story-twitch-2k-wager-challenger-pay — the other side of this flow
- [ ] Marcus copies
-
Platform Observability
story-twitch-2k-wager-observabilitystory: Platform Observability
Role
Platform Operator (Lucas)
Key
observability
Want
As the platform operator, I want golden signal dashboards and uptime probes for the wager service
So That
So that I know immediately when the site is down during Marcus's stream and can respond before challengers notice
Acceptance Criteria
- [ ] Blackbox probe monitors playme2k health endpoint
- [ ] Grafana dashboard shows latency, traffic, errors, saturation
- [ ] Alerts fire on downtime or elevated error rate
Success Metric
MTTR under 5 minutes during stream hours. Zero undetected outages.
Related Architecture
- arch-deployment-twitch-2k-wager — deployment topology
Related
- project-twitch-2k-wager — parent project page
- board-twitch-2k-wager — project board
-
Game Status Visibility
story-twitch-2k-wager-game-statusstory: Game Status Visibility
Role
Challenger
Key
game-status
Want
As a challenger, I want to see on the website that my payment was received and the game is being set up
So That
So that I know the process is working and when to expect my game
Acceptance Criteria
- [ ] After payment: UI shows "Payment confirmed — game queued"
- [ ] During game: UI shows "Game in progress"
- [ ] After result: UI shows winner + payout status
- [ ] Status updates without requiring page refresh
- [ ] Works on mobile
Success Metric
Challenger sees payment confirmed + game queued within 5 seconds of paying.
Related Architecture
- arch-domain-twitch-2k-wager — Game status field
- arch-dataflow-twitch-2k-wager — status update flow
Related
- project-twitch-2k-wager — parent project page
- board-twitch-2k-wager — project board
-
Challenger Twitch Login
story-twitch-2k-wager-challenger-authstory: Challenger Twitch Login
Role
Twitch Viewer (potential challenger)
Key
challenger-auth
Want
As a Twitch viewer, I want to sign in with my Twitch account from the stream description link
So That
So that Marcus knows my gamertag and I don't have to fill out forms
Acceptance Criteria
- [ ] "Login with Twitch" button visible on landing page
- [ ] OAuth redirects to Twitch, returns with username + email
- [ ] Username displayed after login confirming identity
- [ ] Works on mobile (viewers may be on phone while watching stream)
- [ ] Repeat visitors recognized — no re-entering info
Success Metric
Sign in with 1 click from stream link, no manual form entry required.
Related Architecture
- arch-domain-twitch-2k-wager — Challenger entity
- arch-dataflow-twitch-2k-wager — OAuth flow sequence
Related
- project-twitch-2k-wager — parent project page
- board-twitch-2k-wager — project board
-
Winner Payout and Receipt
story-twitch-2k-wager-winner-payoutstory: Winner Payout and Receipt
Role
Winner (challenger)
Key
winner-payout
Want
As a winning challenger, I want to get my buy-in refunded automatically and claim my winnings through a simple payout link
So That
So that I get paid without signing up for anything — refund hits my card, winnings come via CashApp/Venmo/Zelle
Acceptance Criteria
- [ ] Winner declared → Stripe refund of buy-in fires automatically (same card)
- [ ] Winner gets email: "You won $X! Click here to claim your winnings"
- [ ] Payout link goes to /payout/[token] — simple form: CashApp / Venmo / Zelle + handle
- [ ] Payout info stored in Postgres (method + handle)
- [ ] Admin queue shows: "Owe $X to gamertag via CashApp @handle" + "Mark Paid" button
- [ ] Marcus sends money manually, taps "Mark Paid" — payout complete
- [ ] Loser gets email: "Thanks for playing. Receipt attached."
- [ ] When Marcus wins: revenue split tracked (50/50 Marcus/Lucas) — DB only
Success Metric
Winner gets refund within seconds. Payout claimed and sent within 24 hours. Zero Stripe Connect onboarding required.
Related Architecture
- arch-domain-twitch-2k-wager — Payout entity (token, method, handle)
- arch-dataflow-twitch-2k-wager — refund + payout portal flow
Related
- project-twitch-2k-wager — parent project page
- board-twitch-2k-wager — project board
-
Secure Buy-In Payment
story-twitch-2k-wager-challenger-paystory: Secure Buy-In Payment
Role
Challenger
Key
challenger-pay
Want
As a challenger, I want to choose my wager ($15/$30/$45) and pay through a recognizable Stripe Checkout page
So That
So that I feel secure entering payment info on a stranger's website and know exactly what I'm risking
Acceptance Criteria
- [ ] Three buy-in tiers displayed: $15, $30, $45
- [ ] Pot amount (2x buy-in) clearly shown before payment
- [ ] Stripe Checkout (hosted, not embedded) handles all card entry
- [ ] Payout timeline (2-3 business days) visible before committing
- [ ] What happens if you win/lose is clearly stated
- [ ] Payment confirmation shown immediately after successful checkout
- [ ] Webhook confirms payment on backend within seconds
Success Metric
100% of payments go through Stripe Checkout — no custom card forms. Zero payment disputes from unclear terms.
Related Architecture
- arch-domain-twitch-2k-wager — Game, Payment entities
- arch-dataflow-twitch-2k-wager — Stripe Checkout flow
Related
- project-twitch-2k-wager — parent project page
- board-twitch-2k-wager — project board
Architecture 3
-
Deployment: Twitch 2K Wager
arch-deployment-twitch-2k-wagerDeployment: Twitch 2K Wager
Diagram
graph TB subgraph Twitch PS5[Marcus PS5 Stream] TwitchAPI[Twitch API / OAuth] end subgraph Internet Viewer[Viewer Browser] StripeAPI[Stripe API] end subgraph "k3s Cluster" subgraph "twitch-2k-wager namespace" App[twitch-2k-wager-appSvelteKit adapter-nodePort 3000] PG[(Postgres CNPG)] end Funnel[Tailscale Funnelplayme2k.tail5b443a.ts.net] ArgoCD[ArgoCD Sync] end subgraph "pal-e-deployments repo" Kustomize[kustomize overlaytwitch-2k-wager/] end PS5 -->|stream description link| Viewer Viewer -->|HTTPS| Funnel Funnel --> App App --> PG App -->|OAuth + user info| TwitchAPI App -->|Checkout + webhooks| StripeAPI StripeAPI -->|webhook POST| Funnel Kustomize -->|git push| ArgoCD ArgoCD --> AppComponents
Component Purpose Notes twitch-2k-wager-app SvelteKit app (adapter-node) Port 3000. Server routes handle Stripe webhooks, Twitch OAuth callback, admin API. Postgres (CNPG) Game state database CNPG cluster in namespace. Tables: challengers, games, payments, payouts, revenue_splits. Tailscale Funnel Public HTTPS ingress playme2k.tail5b443a.ts.net. Routes to app service. Stripe webhooks and user traffic both enter here. ArgoCD GitOps deployment Watches pal-e-deployments repo. Auto-syncs on push. Kustomize overlay k8s resource definitions Deployment, Service, CNPG Cluster, Secrets, Tailscale Funnel config. Stripe API External payment service Checkout sessions, webhooks, payouts. Credentials from k8s secret. Twitch API External auth + user info OAuth flow + /users endpoint. Credentials from k8s secret. Key Decisions
- Single service (SvelteKit) — same pattern as westside-contracts. Server routes handle API concerns. No separate backend service for MVP.
- Tailscale Funnel for ingress — no cert-manager, no Traefik. Platform standard. Stripe webhooks need public HTTPS endpoint.
- CNPG for Postgres — platform standard. Backups to MinIO. Same operational model as other services.
- pal-e-deployments for GitOps — kustomize overlay + ArgoCD. Same deployment pipeline as all other services.
- Secrets via k8s — Stripe keys, Twitch OAuth credentials stored as k8s secrets. Sourced from ~/secrets at deploy time.
Related
- arch-domain-twitch-2k-wager — entity model
- arch-dataflow-twitch-2k-wager — runtime flows
- project-twitch-2k-wager — project page
- service-onboarding-sop — deployment procedure
-
Domain Model: Twitch 2K Wager
arch-domain-twitch-2k-wagerDomain Model: Twitch 2K Wager
Diagram
erDiagram Challenger { string twitch_id PK string twitch_username string email string stripe_connect_account_id "null until first win" string connect_onboarding_status "none|pending|complete" timestamp created_at } Game { int id PK string challenger_id FK string status "pending|paid|queued|playing|won|lost|cancelled" int buy_in_amount "1500|3000|4500 cents" int pot_amount "double the buy_in" timestamp created_at timestamp completed_at } Payment { int id PK int game_id FK string stripe_checkout_session_id string stripe_payment_intent_id int amount string status "pending|completed|failed|refunded" timestamp created_at } Payout { int id PK int game_id FK string recipient "challenger" int amount string stripe_transfer_id string status "pending|onboarding|initiated|completed|failed" timestamp initiated_at } RevenueSplit { int id PK int game_id FK int marcus_share int lucas_share timestamp recorded_at } Challenger ||--o{ Game : "challenges" Game ||--|| Payment : "has" Game ||--o| Payout : "produces" Game ||--o| RevenueSplit : "tracked when Marcus wins"Components
Component Purpose Notes Challenger A Twitch viewer who wants to play Marcus Identity from Twitch OAuth via Keycloak. stripe_connect_account_id populated on first win (deferred onboarding). Game A single wager match between challenger and Marcus Tiered buy-ins: $15, $30, $45. Pot is always 2x buy-in (house matches). Payment Stripe Checkout payment for the buy-in 1:1 with Game. All amounts in cents. Stripe is source of truth for payment status. Payout Stripe Connect transfer to winning challenger Status "onboarding" = waiting for challenger to complete Connect Express. Once onboarded, transfer executes automatically. RevenueSplit Tracks 50/50 split when Marcus wins Internal DB accounting only. No Stripe Connect needed for Marcus/Lucas split. Key Decisions
- Stripe Connect Express with deferred onboarding — challengers don't KYC until they win. Motivation is high (money waiting). Repeat winners are already onboarded.
- No wallet model — single-game transactions via Stripe Checkout. Simpler than deposit/withdraw. Can add wallet later if volume demands.
- Tiered buy-ins ($15/$30/$45) — challenger picks their wager. Pot is always double. House matches.
- No local auth — Twitch OAuth via Keycloak only.
- Amounts in cents — Stripe convention.
- RevenueSplit is DB-only — no Stripe Connect for Marcus/Lucas. Internal accounting.
Related
- arch-dataflow-twitch-2k-wager — runtime flows
- arch-deployment-twitch-2k-wager — where it runs
- project-twitch-2k-wager — project page
-
Data Flow: Twitch 2K Wager
arch-dataflow-twitch-2k-wagerData Flow: Twitch 2K Wager
Diagram
sequenceDiagram participant V as Viewer (Twitch) participant W as Website (playme2k) participant KC as Keycloak participant T as Twitch API participant S as Stripe participant DB as Postgres participant Op as Operator (Lucas) V->>W: Clicks link in stream description W->>KC: "Login with Twitch" redirect KC->>T: OAuth authorize (user:read:email) T->>KC: Auth code + token exchange KC->>W: OIDC session (username, email, twitch_id) W->>DB: Create/update Challenger record V->>W: Selects buy-in tier ($15/$30/$45) W->>S: Create Checkout Session (amount + metadata) S->>V: Stripe Checkout page (hosted) V->>S: Enters card and pays S->>W: Webhook: checkout.session.completed W->>DB: Create Game (status: paid) + Payment record W->>V: UI: "Payment confirmed — game queued" Op->>W: Declare winner (admin endpoint) W->>DB: Update Game status (won/lost) alt Challenger wins (first time) W->>S: Create Connect Express account (deferred) W->>V: "You won! Complete payout setup to receive $X" V->>S: Connect Express onboarding (bank/debit card) S->>W: Webhook: account.updated (onboarding complete) W->>S: Create Transfer to connected account W->>DB: Create Payout (status: initiated) W->>V: Email: "$X on its way (2-3 days)" else Challenger wins (already onboarded) W->>S: Create Transfer to connected account W->>DB: Create Payout (status: initiated) W->>V: Email: "$X on its way (2-3 days)" else Marcus wins W->>DB: Create RevenueSplit (50/50 Marcus/Lucas) W->>V: Email: "Thanks for playing. Receipt attached." endComponents
Component Purpose Notes Viewer (Twitch) End user watching Marcus's stream Clicks link in stream description. May be on mobile. Website (playme2k) SvelteKit app handling auth, payment, status playme2k.tail5b443a.ts.net. Server routes handle webhooks + admin. Keycloak Identity broker — Twitch OAuth mediation Realm: twitch-2k-wager. Twitch IdP. Handles session + token refresh. Twitch API OAuth provider Scopes: user:read:email. Returns twitch_id, username, email. Stripe Payment + Connect payouts Checkout for buy-ins. Connect Express for winner payouts. Webhooks for both. Postgres Game state persistence Shared CNPG cluster. Stores challengers, games, payments, payouts, revenue splits. Operator (Lucas) Manually declares game results Admin endpoint. No automation for result determination. Key Decisions
- Keycloak brokers Twitch OAuth — app never touches raw OAuth. Standard OIDC session.
- Stripe Connect Express with deferred onboarding — challenger KYCs only after winning. First win triggers onboarding. Repeat winners skip it.
- Two webhook flows — checkout.session.completed (payment) and account.updated (Connect onboarding). Both hit the same webhook endpoint.
- Manual result declaration — operator calls admin endpoint. Simple and reliable.
- Tiered buy-in selection — $15/$30/$45. Stored in Stripe metadata and DB.
Related
- arch-domain-twitch-2k-wager — entity model
- arch-deployment-twitch-2k-wager — where it runs
- project-twitch-2k-wager — project page
Project Page 1
-
Twitch 2K Wager
project-twitch-2k-wagerPlayMe2K
Vision
A peer-to-peer wager platform built for Twitch streams. Any player can challenge any other player — propose a bet, negotiate the terms, and play for real money. The drama IS the product: "my $20 to your $10" proposed live on stream, Marcus deciding whether to take it, chat losing their minds.
Marcus is Player 1, not an admin. The same interface, the same flow, the same energy — whether Marcus is challenging a viewer or a viewer is challenging Marcus. The architecture is player-to-player from day one, enabling friends to bet against each other on the same platform.
Core principles:
- Peer-to-peer, not admin-customer. No special roles. Everyone is a player with a pool, proposals, and games. Marcus happens to be the streamer — the system doesn't care.
- Pool model for instant bets. Players deposit once via Stripe, then bet freely from their pool. Proposals lock funds instantly — no Stripe redirect per game. Withdraw whenever.
- The proposal IS the content. "My $50 to your $10" is trash talk in dollar form. Accept, counter, decline — every interaction is a stream moment. The negotiation is as entertaining as the game.
- Bidirectional proposals. Marcus challenges viewers. Viewers challenge Marcus. Eventually viewers challenge each other. Same flow, same UI, same language.
User Stories
Key Role Success Metric player-pool Any Player Deposit once, bet instantly from pool. No Stripe redirect per game. player-propose Any Player "My $X to your $Y" sent in under 5 seconds. Proposal visible to both sides immediately. player-decide Challenged Player Accept, counter, or decline within the stream moment. Funds lock on accept. game-result Any Player Result declared, pot moves to winner's pool instantly. Email notification sent. player-withdraw Any Player Withdraw pool balance to bank via Stripe payout anytime. observability Operator MTTR under 5 minutes during stream hours. Architecture
Data model (evolved):
Player Pool: player_id → Keycloak user (Twitch OAuth) balance → available to bet (cents) locked → committed to active proposals/games (cents) Proposal: from_player_id → proposer to_player_id → challenged player my_amount → proposer's stake your_amount → challenged player's stake status → pending / accepted / countered / declined / expired Game: proposal_id → accepted proposal player_1_id → from original proposal player_2_id ��� from original proposal pot_amount → my_amount + your_amount result → player_1_win / player_2_win / null Deposit: Stripe Checkout → adds to balance Propose: locks my_amount from proposer's balance Accept: locks your_amount from accepter's balance → creates Game Counter: unlocks original, creates new proposal with different terms Decline: unlocks proposer's amount, no game Result: winner's balance += pot, both locked amounts cleared Withdraw: Stripe payout from balance → player's bankKey decisions: SvelteKit with server routes (adapter-node). Keycloak brokers Twitch OAuth. Postgres on shared CNPG cluster. Service key =
playme2k. Pool model — one Stripe deposit, instant in-database bets. Standard Stripe payouts for withdrawals (2-3 business days).Routes
Critical routes for validation. Every route must return HTTP 200 during post-merge validation.
Route Who What it does /Everyone Landing page — pitch, how it works /playAuthenticated players Dashboard — pool balance, proposals (sent + received), active games, results /depositAuthenticated players Add funds to pool via Stripe Checkout /termsEveryone How it works, payout details, FAQ Board
Status
2026-04-05: MVP live (fixed tiers, pay-per-game, admin/challenger split). Vision evolved to peer-to-peer pool model. 26 items done. Next: proposal flow + pool model (#66), win/lose emails (#60). ArgoCD namespace fix merged (#95). Gmail OAuth configured (playme2kbet@gmail.com).
Milestones
- MVP (done): Fixed tiers, Stripe Checkout per game, admin queue, result declaration, payouts
- v2 (next): Pool model, bidirectional proposals ("my X to your Y"), unified player interface
- v3 (future): Player-to-player (viewers challenge each other), counter-offers, public proposal feed
Repos
Repo Platform Role Status forgejo_admin/twitch-2k-wager Forgejo SvelteKit app + server routes Live in production forgejo_admin/pal-e-services Forgejo Terraform: Keycloak, funnel, CNPG Applied forgejo_admin/pal-e-deployments Forgejo Kustomize overlay (overlays/twitch-2k-wager/) Live
Review 6
-
Review: Move step-by-step flow to /terms page + create terms page
review-831-2026-04-05Verdict: READY
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- present (UX improvement, /terms 404s)
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] User Story -- present, well-formed (challenger wants dedicated terms page)
- [x] Context -- present, clear motivation (action-first /play page)
- [x] File Targets -- 2 files listed with clear descriptions
- [x] Acceptance Criteria -- 4 criteria, all testable
- [x] Test Expectations -- present (200 status, no steps on /play)
- [x] Constraints -- present (keep it simple)
- [x] Checklist -- present
- [x] Related -- references #52 (original steps ticket)
Traceability
- [x] story:challenger-pay label -- present on board item
- [x] story entry verified -- "challenger-pay" row exists in project-twitch-2k-wager user-stories table
- [ ] story note MISSING -- story-twitch-2k-wager-challenger-pay referenced in project page but does not exist in pal-e-docs. Pre-existing gap across all challenger-pay tickets; not a blocker for this ticket.
- [x] arch:twitch-2k-wager-app label -- present on board item
- [ ] arch note MISSING -- No arch-twitch-2k-wager-app note exists. The project has arch-domain-twitch-2k-wager, arch-dataflow-twitch-2k-wager, and arch-deployment-twitch-2k-wager, but no app-specific architecture note. Pre-existing gap; not a blocker for this pure frontend reshuffling ticket.
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#58, open
File Targets
- [x] src/routes/terms/+page.svelte -- NEW file, confirmed: directory does not yet exist (currently 404s as noted). Content to move: step-by-step flow from /play (lines 50-57), payout details and FAQ from landing page (+page.svelte lines 237-281).
- [x] src/routes/play/+page.svelte -- verified: file exists, steps list confirmed at lines 50-57 (ol.steps with 6 li items). Already has a "Terms" link in footer (line 105).
Repo Placement
Correct. Issue filed on forgejo_admin/twitch-2k-wager, all file targets are in this repo. Single-repo scope.
Dependencies
No blockers. Board item #812 (/play guest checkout route) is done. Board item #789 (landing page prototype) is done. No in_progress items conflict. The /terms link already exists in both /play footer (line 105) and landing page footer (line 285) -- currently 404ing, which this ticket fixes.
Acceptance Criteria
All 4 criteria are agent-verifiable:
- "AC1: /terms page exists with step-by-step flow + payout details" -- verifiable by checking route file exists and contains expected content.
- "AC2: /play page shows tiers immediately (no steps above them)" -- verifiable by confirming ol.steps removed from play/+page.svelte.
- "AC3: Terms link at bottom of /play links to /terms" -- already present at line 105. Agent should confirm it still exists after changes.
- "AC4: Mobile-friendly, dark theme" -- verifiable by checking CSS uses existing CSS custom properties (var(--color-*)).
Note: Issue says "payout details, FAQ (from main landing page)" in file targets but does NOT say to remove them from the landing page. Agent should clarify: copy or move? The acceptance criteria say "shows tiers immediately" only for /play, not for landing page. Recommend treating as copy (landing keeps its content, /terms gets the detailed version).
Blast Radius
Low. Changes are purely frontend, two Svelte pages in the same repo. The .steps CSS class is defined in both app.css (global, used by landing page) and scoped in play/+page.svelte. Removing the ol.steps from /play will not affect landing page or other pages. The payout/+page.svelte also uses a .steps class but it is for a different context (payout flow steps) and will not be affected. No API changes, no database changes, no deployment changes.
Decomposition Assessment
2 file targets, 1 repo, 4 acceptance criteria. Well under the three-thing limit and five-minute rule. No independent subtasks that need parallelization. No decomposition needed.
Recommendation
No action needed -- ticket is READY for dispatch.
Non-blocking observations (pre-existing gaps, not specific to this ticket):
- [SCOPE] Create story note story-twitch-2k-wager-challenger-pay (referenced by project page but missing from pal-e-docs). Affects all challenger-pay tickets.
- [SCOPE] Create architecture note arch-twitch-2k-wager-app for the SvelteKit frontend component. Affects all arch:twitch-2k-wager-app labeled tickets.
-
Review: Fix prod pod -> playme2k.tail5b443a.ts.net live
review-825-2026-04-03Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Bug
- [x] Lineage -- present, describes production blocker
- [x] Repo -- present (pal-e-deployments + pal-e-services)
- [x] What Broke -- present, describes ImagePullBackOff / CreateContainerConfigError
- [x] Repro Steps -- present, kubectl + URL check
- [x] Expected Behavior -- present
- [x] Environment -- present (playme2k namespace, prod overlay, ArgoCD-managed)
- [x] File Targets -- present (3 targets listed)
- [x] Acceptance Criteria -- present, 4 criteria
- [x] Test Expectations -- present
- [x] Constraints -- present
- [x] Checklist -- present
- [x] Related -- present, references #3 and #22
All required bug template sections present.
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:deployment-twitch-2k-wager label -- Deployment architecture
- [x] arch note verified -- arch-deployment-twitch-2k-wager exists (id 1156, status active)
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#56, state: open
File Targets
- [ ]
pal-e-deployments/overlays/twitch-2k-wager/prod/harbor-creds.enc.yaml-- ISSUE: Directory does not exist. Notwitch-2k-wageroverlay directory exists in pal-e-deployments. Existing overlays: basketball-api, gcal-scheduler, mcd-tracker, mcd-tracker-app, pal-e-docs, pal-e-mail, pal-e-production, platform-validation, playground, svelte-playground, westside-ai-assistant, westsidekingsandqueens. The kustomize overlay (board item #788, issue #3) has not been created yet. - [ ]
twitch-2k-wager-secrets key names vs deployment-patch.yaml-- Cannot verify: overlay does not exist, so no deployment-patch.yaml to check secretKeyRef entries against. - [ ] ArgoCD app sync -- No ArgoCD application found for playme2k/twitch-2k-wager. Resources appear to have been applied manually (kubectl apply), not via GitOps.
Repo Placement
MISMATCH. Issue is filed on
forgejo_admin/twitch-2k-wagerbut the fix targetspal-e-deployments(kustomize overlay) andpal-e-services(terraform). The issue body correctly identifies these repos in the Repo section, but the Forgejo issue should be filed onpal-e-deploymentssince that is the primary repo where changes land. Since the overlay does not exist yet, this work may actually be a prerequisite completion of issue #3 (Kustomize overlay) rather than a standalone bug fix.Dependencies
- Issue #37 (Fix: prod pod ImagePullBackOff -- Harbor creds) -- still open, overlaps significantly with this issue. Both address the same pod startup failure. #37 focuses on Harbor creds, #56 adds secrets alignment. Pod is now running, so #37 may be resolvable as duplicate/already-fixed.
- Board item #788 (Kustomize overlay, issue #3) -- in needs_approval column. The overlay directory this issue targets does not exist. This ticket implicitly depends on the overlay being created first.
- Board item #826 (Observability: blackbox probe + golden signals, issue #57) -- in backlog. Depends on this ticket (cannot probe a site that is not serving).
- Funnel ingress service name mismatch -- NOT documented in scope. The playme2k-funnel ingress backend points to service name
playme2kbut the actual service is namedtwitch-2k-wager(port 3000). Tailscale operator error:failed to get service "playme2k" for path "/": Service "playme2k" not found. This is the actual blocker preventing the URL from resolving and is not mentioned in the issue body.
Acceptance Criteria
- "Prod pod running (1/1 Ready)" -- ALREADY MET. Pod twitch-2k-wager-7c78c96dc6-mtdlq is 1/1 Ready. Image: harbor.tail5b443a.ts.net/playme2k/app:d4d758f6...
- "Image pulled from harbor.tail5b443a.ts.net/playme2k/app" -- ALREADY MET.
- "https://playme2k.tail5b443a.ts.net/play returns 200" -- NOT MET. HTTP 000 (connection refused). Root cause: funnel ingress backend references non-existent service name
playme2kinstead oftwitch-2k-wager. - "https://playme2k.tail5b443a.ts.net/admin?secret=... shows queue" -- NOT MET. Same root cause as above.
AC 1-2 are already resolved (pod is running and pulling from Harbor). AC 3-4 require fixing the funnel ingress to reference the correct service name. The issue body does not identify this root cause.
Blast Radius
Low. The funnel service name mismatch is isolated to the playme2k namespace. Other services use correct service names in their funnel ingress configs. No downstream consumers affected beyond this project. Rollback is straightforward (kubectl patch or delete/recreate ingress).
Decomposition Assessment
No decomposition needed. Once correctly scoped, this is a single change: fix the funnel ingress backend service name from
playme2ktotwitch-2k-wager. One file target, 2 remaining AC, well under 5 minutes for a single agent. No independent subtasks to parallelize.Recommendation
[BODY]Update "What Broke" to identify the actual remaining blocker: funnel ingress backend references serviceplayme2kbut actual service is namedtwitch-2k-wager. The ImagePullBackOff and secrets issues appear already resolved.[BODY]Update file targets: the overlay directorypal-e-deployments/overlays/twitch-2k-wager/does not exist. Either this ticket depends on #3 (overlay creation) completing first, or the fix is a kubectl patch to the existing manually-applied ingress plus a corresponding update to wherever the funnel is defined in Terraform (pal-e-services).[BODY]Add file target: the funnel ingress resource needsspec.defaultBackend.service.namechanged fromplayme2ktotwitch-2k-wager.[BODY]Update AC: Mark AC 1-2 as already passing. Focus remaining AC on the funnel routing fix.[BODY]Document the dependency on issue #3 (kustomize overlay) or clarify that this fix will be applied via kubectl/Terraform and the overlay comes later.[SCOPE]Consolidate or close issue #37 (ImagePullBackOff) -- the pod is now running and pulling images successfully.
-
Review: Checkout API: guest flow (gamertag + email)
review-813-2026-04-03-r4Verdict: 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_idreferences. 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.
- [x]
-
Re-Review: /play guest checkout route + landing updates
review-812-2026-04-03-r2Verdict: READY
Re-review of board item #812 after body fixes addressing two NEEDS_REFINEMENT findings from
review-812-2026-04-03.Previous Findings — Resolved
- [x] [BODY] Undocumented dependency on #39 — FIXED. Lineage section now states "Must merge WITH or AFTER #39 (guest checkout API)." Dedicated Dependency paragraph in Context explains the payload mismatch (
gamertag+emailvstwitch_id+twitch_username). Related section lists#39 — must merge first (guest checkout API). Three separate callouts — dependency is thoroughly documented. - [x] [BODY] Missing migration note for $1 tier CHECK constraint — FIXED. Migration note paragraph documents the exact ALTER TABLE commands needed:
DROP CONSTRAINT ... ADD CONSTRAINT ... CHECK (buy_in_amount IN (100, 1500, 3000, 4500)).
Template Completeness
- [x] Type — Feature
- [x] Lineage — "New primary flow — direct challenge link. Must merge WITH or AFTER #39."
- [x] Repo — forgejo_admin/twitch-2k-wager
- [x] User Story — clear challenger wants frictionless guest checkout
- [x] Context — explains /play route, landing CTA change, dependency on #39, migration strategy
- [x] File Targets — 6 files, all specific with change descriptions
- [x] Acceptance Criteria — 6 testable conditions
- [x] Test Expectations — 3 items including CI pipeline
- [x] Constraints — code exists locally, depends on #39, DB migration needed, no new deps
- [x] Checklist — standard PR/tests/no-unrelated
- [x] Related — project page, story notes, arch note, #39 with ordering note
Traceability
- [x] story:challenger-pay label — Secure Buy-In Payment
- [x] story note verified — challenger-pay listed 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 (id 1155) exists in pal-e-docs, active
- [x] Forgejo issue — forgejo_admin/twitch-2k-wager#38, open
File Targets
- [x]
src/routes/play/+page.svelte— verified: NEW, 237 lines, tier pills, gamertag + email form, handleCheckout calling /api/checkout with { tier, gamertag, email } - [x]
src/routes/+page.svelte— verified: MODIFIED, clickable tier cards, $1 test tier (dashed red border), gamertag input, checkout form replacing Twitch-login-first CTA - [x]
src/app.css— verified: MODIFIED, .tier-card, .tier-card.selected, tier color vars present - [x]
src/lib/stripe.ts— verified: MODIFIED, $1 test tier (amount: 100, label: "Test Challenge") in TIERS map - [x]
src/lib/types.ts— verified: MODIFIED, BuyInAmount = 100 | 1500 | 3000 | 4500 - [x]
src/lib/schema.sql— verified: MODIFIED, CHECK (buy_in_amount IN (100, 1500, 3000, 4500))
Repo Placement
Correct. Issue filed on forgejo_admin/twitch-2k-wager, all 6 file targets in the same repo. No cross-repo scope.
Dependencies
- [x] Board item #813 / Forgejo issue #39 ("Checkout API: guest flow — gamertag + email") — DOCUMENTED. Issue body states merge order: #39 first or simultaneously. Both items currently in backlog on board-twitch-2k-wager. Dependency is now explicit in Lineage, Context, Constraints, and Related sections.
Acceptance Criteria
6 AC, all individually testable. AC #3 ("Pay $X & Play button calls /api/checkout") depends on #39 being merged for full end-to-end validation, which is now documented. An implementing agent can verify frontend behavior and mock/stub the endpoint for AC #3 if #39 is not yet merged. Testable and complete.
Blast Radius
- $1 test tier is consistent across all three type-system files (stripe.ts, types.ts, schema.sql). No mismatch.
- Schema migration strategy now documented with exact ALTER TABLE commands.
- Landing page CTA change (login-first to gamertag-first) does not break existing authenticated flow — Twitch login preserved as optional.
- Rollback is straightforward — revert branch. No DB schema applied yet.
Decomposition Assessment
6 file targets in 1 repo, 6 AC. Code already written locally — agent work is commit + push + CI. Well under 5 minutes. No decomposition needed.
Recommendation
No action needed. Both previous findings resolved. Ticket is ready for execution.
- [x] [BODY] Undocumented dependency on #39 — FIXED. Lineage section now states "Must merge WITH or AFTER #39 (guest checkout API)." Dedicated Dependency paragraph in Context explains the payload mismatch (
-
Review: Spike: Stripe Checkout + webhook + payout (re-review)
review-787-2026-04-03-r2Verdict: READY
Re-review after issue body re-scoped and all backing notes created. Previous review:
review-787-2026-04-03.Template Completeness
Validated against
template-issue-spike(spikes have no File Targets or Test Expectations):- [x] Type -- Spike
- [x] Lineage -- Standalone, validates payment architecture
- [x] Repo -- forgejo_admin/twitch-2k-wager
- [x] Question -- Clear three-part question (buy-in collection, winner payout, revenue split)
- [x] What to Explore -- 5 detailed exploration areas with specific experiments
- [x] Success Criteria -- 5 verifiable criteria
- [x] Time-box -- 2 hours, with clear rationale ("Payment collection is straightforward. Payout mechanics are the unknown.")
- [x] Related -- 7 items (project page, 2 arch notes, 2 story notes, 2 sibling issues)
All required sections present per
template-issue-spike. No missing fields.Traceability
- [x] story:challenger-pay label -- "Secure Buy-In Payment" (
story-twitch-2k-wager-challenger-pay) - [x] story note verified -- found in project-twitch-2k-wager user-stories table (key: challenger-pay)
- [x] story:winner-payout label -- "Winner Payout and Receipt" (
story-twitch-2k-wager-winner-payout) - [x] story note verified -- found in project-twitch-2k-wager user-stories table (key: winner-payout)
- [ ] arch:stripe-api label -- NO BACKING NOTE.
arch-stripe-apidoes not exist in pal-e-docs (404). The issue body correctly referencesarch-domain-twitch-2k-wagerandarch-dataflow-twitch-2k-wager, which both exist. [LABEL] Replacearch:stripe-apiwitharch:domain-twitch-2k-wager,arch:dataflow-twitch-2k-wager. - [x] arch-domain-twitch-2k-wager -- verified, exists (Domain Model with Mermaid ER diagram, Payment/Payout/RevenueSplit entities)
- [x] arch-dataflow-twitch-2k-wager -- verified, exists (Data Flow with Mermaid sequence diagram, Stripe Checkout flow)
- [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#2, state: open
File Targets
N/A -- Spike type. No file targets expected per
template-issue-spike. Reference to~/secrets/stripe/test-secret-keyverified: file exists with valid Stripe test key.Repo Placement
OK. Issue filed on
forgejo_admin/twitch-2k-wager, Repo field matches. Spike investigates Stripe integration for this repo. Single-repo scope. No cross-repo concerns.Dependencies
Board has 5 items, all in backlog. No blocking dependencies for this spike:
- [x] #786 Service onboarding (Keycloak + CNPG) -- logical prerequisite for deployed testing, but NOT a blocker (Stripe CLI provides local webhook testing)
- [x] #788 Kustomize overlay -- sibling infra, not blocking
- [x] #789 Landing page prototype -- downstream consumer of spike findings, not blocking
- [x] #792 Monitoring -- unrelated at spike stage
Spike can proceed independently. Stripe test mode + CLI provides full local testing without deployed infrastructure.
Acceptance Criteria
5 success criteria, all verifiable by an agent:
- [x] "Working Stripe Checkout test flow" -- verifiable: create session via API, receive webhook via Stripe CLI
- [x] "Payout mechanism decided with evidence" -- verifiable: documented comparison matrix (Connect vs manual vs hybrid)
- [x] "Revenue split tracking approach decided" -- verifiable: documented decision (DB fields vs Stripe metadata)
- [x] "Tiered pricing validated" -- verifiable: test sessions at $15/$30/$45 amounts
- [x] "Follow-up feature tickets created" -- verifiable: check Forgejo for new issues post-spike
All criteria are concrete and agent-testable. No ambiguous language. No missing criteria.
Blast Radius
Spike produces knowledge, not code changes. Stripe test mode is fully isolated from live keys. No downstream systems affected. Zero risk.
Decomposition Assessment
Apply the three-thing limit and five-minute rule:
- File targets: 0 (spike, not implementation)
- Success criteria: 5 (within bounds for investigation work)
- Repos touched: 1
- Time-box: 2 hours (spike-appropriate, not agent-implementation scope)
- Independent subtasks: No -- exploration areas are sequential (buy-in informs payout which informs revenue split)
No decomposition needed. Single agent session can complete this spike.
Recommendation
[LABEL]Replace board item labelarch:stripe-apiwitharch:domain-twitch-2k-wager,arch:dataflow-twitch-2k-wager-- the actual architecture notes referenced in the issue body. The current label has no backing note in pal-e-docs.
This is a minor label correction. The scope itself is solid -- all backing notes exist, traceability is complete in substance, the template is fully populated, and the spike is well-scoped for a single agent pass. Once the label is corrected, this ticket is fully READY.
-
Review: Landing page HTML/CSS prototype (re-review)
review-789-2026-04-03-r2Verdict: READY
Re-review of board item #789. Previous review (
review-789-2026-04-03) verdict was NEEDS_REFINEMENT with 3 [SCOPE] recommendations. All story/arch notes now exist. Issue body updated with Keycloak context and corrected pot amounts.Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Standalone, first UI work
- [x] Repo -- forgejo_admin/pal-e-playground
- [x] User Story -- clear who/what/why
- [x] Context -- playground-first pipeline, Keycloak-brokered Twitch login noted, pot amounts corrected to $30/$60/$90
- [x] File Targets -- 3 files to create (index.html, style.css, game-status.html) in twitch-2k-wager/
- [x] Acceptance Criteria -- 8 testable items
- [x] Test Expectations -- visual review at 390px + Lucas approval
- [x] Constraints -- pure HTML/CSS, no Tailwind, dark theme, mobile-first
- [x] Checklist -- PR opened, tests pass, no unrelated changes
- [x] Related -- project page, story notes, arch notes listed
All required sections for Feature template present and complete.
Traceability
- [x] story:challenger-auth label -- present on board item
- [x] story note verified --
story-twitch-2k-wager-challenger-authexists (id 1157, type user-story, status active). RESOLVED from previous review. - [x] story:challenger-pay label -- present on board item
- [x] story note verified --
story-twitch-2k-wager-challenger-payexists (id 1158, type user-story, status active). RESOLVED from previous review. - [x] Both stories listed in project-twitch-2k-wager user-stories table with links and success metrics.
- [x] arch:twitch-2k-wager-app label -- present on board item
- [x] arch notes verified --
arch-deployment-twitch-2k-wagerexplicitly describes the twitch-2k-wager-app component (SvelteKit adapter-node, port 3000, server routes). The app is fully documented across the three architecture notes (domain, dataflow, deployment). No separate arch-twitch-2k-wager-app note exists, but the deployment note IS the app's architecture documentation. Acceptable for a playground prototype ticket -- the arch label traces to a real, documented component. - [x] Forgejo issue -- forgejo_admin/twitch-2k-wager#4, open
Note: Issue Related section references
story-twitch-2k-wager-game-statuswhich does not yet exist as a note in pal-e-docs (the project page lists it in the user-stories table but the note itself is missing). This is NOT a blocker for this ticket -- game-status is a separate story with its own future ticket. The game-status.html file target in this ticket is a static prototype, not the real implementation.File Targets
- [x] twitch-2k-wager/index.html -- new file in pal-e-playground; parent repo exists at ~/pal-e-playground, directory will be created
- [x] twitch-2k-wager/style.css -- new file in same directory
- [x] twitch-2k-wager/game-status.html -- new file in same directory
All targets are new files in a new subdirectory. pal-e-playground repo confirmed to exist locally.
Repo Placement
OK. Issue filed on forgejo_admin/twitch-2k-wager (project repo). Work targets forgejo_admin/pal-e-playground. Correct per playground-first pipeline convention. Single repo scope.
Dependencies
- [x] No blocking dependencies -- pure HTML/CSS with no backend requirements
- [x] Board items #786 (service onboarding), #787 (Stripe spike), #788 (kustomize overlay), #792 (monitoring) are all in backlog and none block this prototype
- [x] No items currently in_progress that could conflict
Acceptance Criteria
8 criteria, all appropriate for playground prototype:
- [x] Wager concept communication -- subjective, gated by Lucas approval
- [x] Three buy-in tiers ($15/$30/$45) with pots ($30/$60/$90) -- verifiable in markup. Amounts now consistent between issue body and architecture notes.
- [x] Login with Twitch button -- verifiable in DOM. Issue now clarifies Keycloak-brokered auth (user sees Twitch branding).
- [x] Payout timeline -- verifiable in content
- [x] Win/lose outcomes -- verifiable in content
- [x] Mobile-first 390px -- verifiable via devtools
- [x] Dark theme Twitch vibe -- subjective, gated by Lucas approval
- [x] Game status page content -- verifiable in game-status.html
All criteria testable. Visual items gated by Lucas approval in Test Expectations.
Blast Radius
Minimal. 3 new files in a new directory within pal-e-playground. No existing code modified. No downstream consumers. Fully reversible.
Decomposition Assessment
3 files, 1 repo, 1 directory. 8 acceptance criteria but all are content/visual checks on 3 tightly coupled files. Well within 5-minute rule. No decomposition needed.
Recommendation
No action needed. All [SCOPE] items from previous review have been resolved. Ticket is READY for execution.
Informational (non-blocking):
story-twitch-2k-wager-game-statusnote referenced in issue Related section does not exist yet. Should be created when the game-status ticket is scoped. Not a blocker for this landing page prototype.
Board 1
-
Twitch 2K Wager
board-twitch-2k-wagerNo content