Neil Godfrey

Tinley Park · May 18, 2026

Tuesday, May 12, 2026

mood: closing-out

Phase 1 of the manifestation app crossed the line on all four layers today. Code shipped. Schema healed. A live charge propagated through the stack and lit up a real subscription. Then I sat with the visible bug on the post-login route for two hours before the actual cause turned out to be three layers deeper than where I was looking.

Worked on

  • Reviewed gl-elevatedaily — what to work on next? Found the web-next cutover had already silently completed; the first recommendation was decommissioning the stale Astro path
  • Deleted the legacy web/ Astro tree — 494 MB, 13,649 files
  • Fixed prod OG-image leakage — every prerendered page was baking og:image: http://localhost:3000/... because the build-time public env vars weren't wired through compose's build.args to the builder stage
  • Deleted stale S3 bucket from the pre-cutover deploy path
  • Shipped internal-auth Phase 1a + 1b end-to-end — hybrid X-Internal-Token + Bearer dispatcher, bootstrapped a new BFF client + internal-bff realm role, expanded the realm URL default to a 2-issuer allowlist so server-to-server callers fetching tokens with the public issuer claim don't get 401'd
  • Wrote the Bearer caller in the BFF — InternalBearerProvider + bearer-first / legacy-fallback dispatcher
  • Added the api/tests/ scaffold with 14 unit tests, including a 20-coroutine concurrency-against-asyncio-Lock check on the bearer provider
  • Scheduled Phase 2 cleanup via DELETE-IN-PHASE-2 code markers in both repos
  • Diagnosed the post-login route's React #418 hydration crash — chased a CORS configuration bug for two rounds before reading the actual server response
  • Backfilled the schema_migrations table — 9 migrations had silently drifted on a live revenue surface; the README promised a runner that was never built
  • Reviewed the Phase 1 backlog — 7 stories that shipped weeks ago were still "Todo" in the tracker

Shipped

  • Internal auth Phase 1a + 1b live end-to-end — the BFF can now call the elevatedaily api with a real Bearer token through the public Keycloak issuer
  • Phase 1 closed on all four layers — code shipped, schema healed, live charge propagated ($8.88 → active subscription, message id captured), user-facing route visually confirmed after the hydration fix
  • The 9-migration drift surfaced — backfilled schema_migrations so a real runner has a starting point. The runner itself is filed as follow-up work, not invisible debt anymore
  • Seven backlogged Stories from 01.1–01.7 closed with per-ticket verification comments citing shipped file paths, PRs, and live evidence — the backlog now reflects reality instead of weeks-stale state
  • 2-issuer allowlist on the realm URL — both the public issuer claim and the in-cluster one resolve to the same realm now

Got stuck

  • CORS-error red herring on /today. Two diagnosis rounds chased a CORS configuration bug before docker logs on the api showed the actual response was a 500. FastAPI / Starlette strip CORS headers off server errors by default, which makes the browser-side error look exactly like a misconfigured CORS policy. Lesson: when a CORS error claims a header is missing on a real endpoint, read the server log before touching CORS config
  • Schema-drift on a revenue surface — 9 migrations had silently drifted. README promised a runner that was never built. Now a real ticket with the backfill as the runner's starting state. Invisible debt that survives weeks tends to be load-bearing somewhere; flushing it to a ticket is the cheapest fix
  • Browser extension was a false suspect. lockdown-install.js SES injection appeared in the console alongside React #418 and looked plausible; Incognito repro killed that theory immediately. Lesson: when a hydration error has a console neighbor that looks suspicious, validate with Incognito first — it's faster than reading the source
  • Silent shadowing bug found mid-flight: a module imported from config import get_settings AND defined an async def get_settings route handler in the same file. Python late-binding meant the route handler shadowed the imported function only inside that module's scope at call time. The fix is rename one or the other; the durable lesson is that route handlers are not safe to name after frequently-imported helpers
  • Direct push to main was correctly blocked by the auto-mode classifier during the Phase 1 closeout work. Every closeout commit landed via feature branch + PR + squash — including for stale-state tracker cleanups

Tomorrow

  • Off-host backup for the elevatedaily Postgres — Phase 1 DoD has an explicit gate that's still open
  • Migrate-runner + CI drift check — codify so the next 9-migration silent drift gets caught at PR time
  • CORS-on-500 middleware fix — the underlying middleware ordering is wrong; synthetic 500s should pick up the CORS headers on the way back out
  • First $100 MRR — distribution / marketing problem, not a code problem

Notes

Phase 1 of the manifestation app crossed the line on all four layers today. The code had been shipped for weeks. The schema healed once I noticed the drift. A live $8.88 charge propagated cleanly through Stripe → subscription row → unlocked UI. The post-login route rendered correctly in prod after the React #418 fix landed. Four months of work closed in one afternoon.

The slowest part was sitting with that one visible bug on /today for two hours and being wrong about where it lived. The browser said CORS. I believed the browser. Two diagnosis rounds in, the actual response was a 500 — and FastAPI strips CORS headers off server errors. The browser was telling the truth about what it saw; the cause was three layers deeper than the symptom. The durable rule from this: read the server log before touching CORS config. Browsers describe the wall, not what's behind it.

The 9-migration drift was the other surprise. The repo's README promised a migration runner. The runner had never been built. Nine migrations had silently applied to prod without ever being tracked in the schema_migrations table. Backfilled the table so a real runner has a starting state, and filed the runner itself as a real ticket. Invisible debt that survives weeks tends to be load-bearing somewhere. Flushing it to a ticket is the cheapest fix.

The Phase 1 backlog cleanup felt small but isn't. Seven stories that had shipped weeks ago were still marked Todo. The state of the tracker mattered less when the work was in flight — but the moment Phase 1 closed, the backlog became the input to "what's Phase 2?" and a stale backlog gives a stale answer. Each closure got a verification comment citing the file paths, PRs, and live evidence. Future-me needs to know why it's closed, not just that it is.

The "first $100 MRR" gate is still open and explicitly a different shape of work. The product is shipped. The distribution isn't. That's a marketing flywheel, not an engineering ticket. Naming that boundary out loud is part of closing Phase 1 cleanly — what's done, what's next, and who owns the next.