Phase A Review & Handoff (Kenobi/Fable 5 → Opus 4.8)
Reviewed 2026-07-06 by Kenobi (Fable 5) before executor handoff. Scope: every line of app/ built in stages A1–A5.
Verdict
Sound. One real defect found and fixed during review; everything else is either working as designed or a consciously deferred risk listed below. All 8 packages green (run twice for idempotency), live stack verified, 30 commits, every mutation audited.
Fixed during review
- Admin self-lockout — an admin could deactivate or demote their own account on
/admin/users, orphaning the system. Guard added (refuses self-deactivation/self-demotion), test-first.fix(admin): refuse self-deactivation/self-demotion.
Accepted risks / deferred items (the honest register)
| # | Item | Why it's OK for now | Falls due |
|---|---|---|---|
| 1 | No CSRF tokens | SameSite=Lax cookie + POST-only mutations + tailnet-only exposure | D3 hardening (TODO comment at templates.go pageData) |
| 2 | No login rate limiting | bcrypt cost is the throttle; failures audited with usernames; 7 users on a tailnet | E2 (Cloudflare Access will front auth anyway) |
| 3 | Templates parsed per request | Milliseconds at 7-user scale; simplifies dev | D3 polish, only if felt |
| 4 | Advertiser/agency delete unguarded | No orders exist yet to reference them | B5 MUST add the guard — TODO(B5) at both delete sites in sponsors.go |
| 5 | Moving an episode onto a cancelled episode's date errors (unique constraint) | Loud, not silent; the workflow answer is "reinstate instead" | Revisit if Debi hits it in practice |
| 6 | Superseded 2026-07-06 by the whole-dollar house rule: all amounts round to nearest dollar via roundToDollar — inputs, computations, displays |
Closed | |
| 7 | pctToBP parses percent via float64 then math.Round |
It's a rate (basis points), not money; deterministic for 2-decimal inputs | Accepted |
| 8 | Cancel/reinstate reflows future episode numbers instantly | Exactly Leo's rule; flagged to Leo 2026-07-06 with the external-reference caveat | Warning banner if it ever bites |
| 9 | Backup is nightly pg_dump to ~/Backups/twit-ads (local disk) |
Testbed; revenue data is still mostly sample | Leo: repoint BACKUP_DIR at a NAS-synced path before Checkpoint 1 (real orders) |
| 10 | login_failed audit rows record attempted usernames |
Useful diagnostics (proved itself day one); internal system | Accepted |
Architecture notes for the next executor (Opus 4.8)
- Read
CLAUDE.md, the Specification, and your stage plan before every stage. Execute ONE stage, STOP for Leo's gate. The decisions logs at the bottom of each executed plan are the project's institutional memory — keep writing them. - Established idioms (copy them, don't reinvent): store pattern (
internal/auth/users.go— const cols, scan helper, wrapped errors,TRUNCATE-based test helpers,TEST_DATABASE_URLskip); handler pattern (Depsmethods, interface-per-store inserver.go, fakes in_test.go); audit from handlers, never from stores; templates perusers.html; role gates inmiddleware.go. - Computed-over-stored is a live design principle here (episode numbers). When B-phase needs derived values (totals, availability), compute them — store only facts.
- Money: integer cents,
dollarsToCents/centsToDollarsininternal/server/money.go. NEVER floats. Rates resolve as-of and get copied onto order lines. - Test gotchas already solved — don't regress them:
just test-dbruns-p 1(shared DB, packages must not run concurrently); the rates test helper re-seeds products after truncate;.gitignorenegation keeps migration.sqlfiles tracked against Leo's global ignore. - Watch
go mod tidy: it silently re-upgraded a pinned dep once (goose is pinned to v3.27.1 for the 14-day rule; re-check after any tidy).
Where Fable should be called back in (recommendation to Leo)
- B1 plan review + golden tests (before Opus implements) — the money-math core is the highest-stakes design in the system; the golden cases come from Lisa's interview examples.
- B5 design + post-implementation adversarial review — transactional finalize, the oversell impossibility proof, sea-of-red. Concurrency bugs here cost real revenue.
- Checkpoint 1 (after B6, Lisa's parallel week) — folding her feedback into spec adjustments.
- End-of-phase code reviews (end of B, end of C) — like this one.
- E0 legacy-import mapping — the legacy schema knowledge lives in the analysis docs, but the mapping decisions deserve senior eyes.
- E2 production cutover review — with Russell.
- Any debugging that stumps Opus twice — don't let it thrash; escalate with the failing test.