SPRINT-2 Gate F Report — Closeout + SPRINT-3 Kickoff¶
Status: SPRINT-2 CLOSED — awaiting Ryan SPRINT-3 go
Date: 2026-08-28
Prerequisite: Gate E pass (Ryan E-1..E-4)
Summary¶
SPRINT-2 (RealOutcomes auth unification via auth.last1.id) is complete. VM 117 GoTrue is retired. RealOutcomes on VM 116 authenticates exclusively through Last1 ID OIDC. Ryan verified both primary accounts, app surfaces, and network isolation (zero traffic to VM 117 /auth/v1/*).
Next: SPRINT-3 (realoutcomes-edge tunnel + realoutcomes.app production cutover). No execution until Ryan replies SPRINT-3 go.
Gate F verification (Ryan)¶
| # | Test | Result |
|---|---|---|
| E-1 | ryan@last1.enterprises login, admin badge, Veterans watchlist |
PASS |
| E-2 | ryan@last1.org login, id 536a1797… unchanged |
PASS |
| E-3 | Directory, watchlists, portfolio load | PASS |
| E-4 | Auth traffic only to auth.last1.id; no VM 117 :8000/auth/v1/* |
PASS |
SPRINT-2 deliverables (Gates A–F)¶
| Gate | Work | Report |
|---|---|---|
| A | app_clients + BWS + REALOUTCOMES_APP_SECRET |
SPRINT-2_GATE_A_REPORT.md |
| B | Last1 ID SDK + JWKS server auth | SPRINT-2_GATE_B_REPORT.md |
| C | Deploy VM 116 + OAuth smoke | SPRINT-2_GATE_C_REPORT.md |
| D | User ID FK remap + login verification | SPRINT-2_GATE_D_REPORT.md |
| E | Retire VM 117 GoTrue | SPRINT-2_GATE_E_REPORT.md |
| F | Closeout (this report) | — |
Execution plan: SPRINT-2_REALOUTCOMES_AUTH_EXECUTION_PLAN.md
Pattern lesson: Gate D UUID correction (52ce85f9 vs 396e95ce)¶
What happened¶
During Gate D FK remap for ryan@last1.enterprises, an initial target UUID 52ce85f9… was taken from an auth.identities (or secondary identity) row. The live OAuth id_token.sub uses public.identities.id = 396e95ce-abac-42e2-9980-c08f19a1af1d. Remap to the wrong id would have left child rows (watchlists, admin profile, search history) orphaned from the identity Ryan actually logs in with.
The error was caught in-session and corrected to 396e95ce… before Ryan login verification.
Root cause¶
| Mistake | Why it fails |
|---|---|
| Infer remap target from any identity table row matching email | Multiple UUIDs can exist per email (legacy Supabase row, orphan OAuth upsert, provider linkage rows) |
Trust Supabase-era public.users.id |
Pre-OAuth ids (d1034389…) do not match sovereign id_token.sub |
Trust auth.identities.id without confirming token claim |
OAuth issuer emits sub from public.identities.id, not necessarily every related row |
Correct pattern (binding for future product migrations)¶
- Source of truth for remap target: decode a real OAuth
id_token(or/api/auth/userafter one successful login) and usesubverbatim. - Pre-remap:
pg_dumpfullpublic+authschemas; document every FK column touchinguser_id. - Post-remap: assert one
public.usersrow per email; assertusers.id = subfor each test account; assert zero orphan FK rows. - Never remap until Gate C OAuth loop proves which
subthe app persists (callback upsert path is authoritative). - Cross-check last1.id PostgREST:
identities.idfor email must equal chosen remap target before transaction.
One-line rule¶
Remap to
id_token.sub, not to "an identity UUID we found in the database."
Document in runbooks for SPRINT-6 (Onset, real users) and any future FK migration.
DB / infra state at SPRINT-2 close¶
| Component | State |
|---|---|
| RealOutcomes auth | auth.last1.id OIDC (PKCE + JWKS) |
| VM 117 GoTrue | Retired (rollback backup on VM 117) |
| VM 117 stack | Postgres, Envoy, REST, Storage, Edge Functions, Supavisor — running |
App DATABASE_URL (VM 116) |
Still hosted Supabase (Gate D remap target); VM 117 DB is rehearsal stack until T3/SPRINT-5 cutover |
| Prod DNS | realoutcomes.app still on Railway until SPRINT-3 |
SPRINT-3 pointer¶
Plan for Ryan review: SPRINT-3_REALOUTCOMES_EDGE_EXECUTION_PLAN.md
Reply SPRINT-3 go to authorize tunnel install + production DNS cutover.
END Gate F / SPRINT-2 closeout