11 KiB
Session 0047.0 — Transcript
Date: 2026-05-30 Started: 2026-05-30T11-02 · Ended: 2026-05-30T12-47 (PST implied) Goal: Advance ROADMAP #38 (Track Δ) — turn the session-0046 e2e scaffold (rfc-app PR #4) into a working, green Tier-1 local-Docker suite. No VM work (Track Δ's VM pieces stay gated/deferred to their own session).
Outcome: #38 Tier 1 is live and green.
docker compose up --buildstands up the full stack (frontend+backend+SQLite+Mailpit+real disposable Gitea) and the Playwright suite passes 4/4, deterministically across repeated runs: health, landing render, and the OTC sign-in vertical (API round-trip + full browser UI). Pushed to rfc-app PR #4 (session-0046/e2e-tier1-scaffold, fast-forwarda3f48ba..15af891). One default-secure framework change rode along (ratelimit.pytest bypass). Not a release; no deploy; PR #4 left for the operator to merge.
Pre-session state
- flotilla:
main@5f1689f(v1.4.0). v1 has shipped — relevant below. - rfc-app:
main@0c972c8(v0.31.0). PR #4 open from session 0046: branchsession-0046/e2e-tier1-scaffold@a3f48ba, ane2e/scaffold (compose topology + helpers +test.fixmespecs) — not yet brought up or validated; Dockerfiles + the Gitea bootstrap were the explicit TODO. - OHM live: v0.31.0 (deploys.id=46). Untouched this session.
- Other sessions in flight at claim time: 0045 (
--INPROGRESS, UX pass on the admin Users page — rfc-app frontend, disjoint frome2e/), plus stale--INPROGRESSplaceholders 0026/0027.
Turn-by-turn arc
Arc 1 — Orient + the v1-gate question
Read SESSION-PROTOCOL.md + the ROADMAP Track-Δ section (#37–#42). Claimed
session 0047 via claim-session-id.sh (placeholder pushed at e76b1d3).
The launch prompt insisted on confirming flotilla v1 timing before any VM-side work. Asked the operator (Tier-1-only vs PPE-in-scope). The operator turned it back to me: "can we do #2 this session or stick to #1?"
My call: stick to Tier-1 (#1) this session. Surfaced a factual
correction — flotilla v1 has already shipped (v1.4.0), so the
"gated on v1" condition on Track Δ is already satisfied; #37 (PPE) isn't
blocked by v1 timing, it's just a large, separate live-infra effort (new GCP
project + micro VM + colocated ppe.git Gitea + DNS + a real flotilla
multi-deployment code change). #38 Tier-1 is prerequisite-free, operator-
gesture-free, and de-risks PPE (Tier 2 reuses the same suite). House
discipline is one feature per session.
Arc 2 — Map the backend (Explore subagent)
Dispatched an Explore agent to produce a concrete reference: config.py
required vars, boot/migration path (app.main:app, db.run_migrations),
OTC endpoints (POST /auth/otc/request → 202; POST /auth/otc/verify →
session cookie rfc_session), the Turnstile knob (TURNSTILE_REQUIRED),
the gitea.py client surface, the seed scripts, the frontend build
(VITE_APP_NAME required), /api/health, and the deploy/nginx proxy
shape. Verified the load-bearing details directly afterward.
Key catch: the rfc_session cookie is Secure by default → browsers
drop it on http://localhost → sign-in wouldn't stick in Tier 1. Confirmed
the opt-out is SESSION_COOKIE_SECURE=false (main.py:152).
Arc 3 — Build the Tier-1 stack (in an isolated worktree)
Per the parallel-session rule (0045 in flight, same repo), worked in a
dedicated git worktree on session-0047/e2e-tier1 (off the scaffold tip),
never the shared checkout. Wrote:
e2e/docker/backend.Dockerfile(+backend-entrypoint.sh) — repo-mirror layout soparents[2]/VERSIONand the seed scripts'parent.parentresolve as in prod; entrypoint reads the minted bot token intoGITEA_BOT_TOKEN. Same image runs thegitea-seedone-shot.e2e/docker/frontend.Dockerfile+nginx.conf— Vite build → nginx serving the SPA + proxying/api,/authtobackend:8000.scripts/bootstrap_e2e_gitea.py— create the org, then reuseseed_meta_repo.pyto create + seedMETA_REPO(ohm-content).- Rewrote
docker-compose.yml:gitea(realgitea/gitea:1.22),gitea-init(mint bot user+token via CLI → shared volume),gitea-seed(org + seed),backend,frontend,mailpit, chained bydepends_on. .env.example: addedSESSION_COOKIE_SECURE=false,GITEA_ADMIN_PASSWORD, annotated the runtime-overriddenGITEA_BOT_TOKEN; root.dockerignore.
Arc 4 — Bring it up; three real obstacles, fixed
Iterated against a live docker compose up:
- Gitea refuses to run as root → every
gitea admin user createFATAL'd. Fix: container is root only tochownthe fresh token volume; thegiteacommands drop to thegituser viasu-exec. - gitea-init not idempotent on an
upover a persisted volume (user + token-namee2ealready existed → exit 1). Fix: short-circuit if the token file exists; tolerate an existing user; unique token name. - Per-IP OTC rate limiter (
5 sends / 5 min,ratelimit.py) — the suite drives all auth from one IP, so a normal run + CI retries trip it. This is a real determinism issue, not just test churn. Fix: a default-secure, env-gatedRFC_APP_RATELIMIT_DISABLEDbypass inallow()(mirrorsRFC_APP_INSECURE_WEBHOOKS; durable per-email lockouts unaffected) +tests/test_ratelimit_bypass.py(incl. the default-still-enforces case, run green via a throwaway venv).
After fixes: clean down -v && up --build → all services healthy,
/api/health → {"version":"0.31.0","status":"ok"}, gitea seeded.
Arc 5 — Green the suite
Finalized the test.fixme specs against the live DOM:
smoke.spec.js: health shape + landing<h1>/title.otc-login.spec.js: API round-trip (request→Mailpit→verify→/me, assertspending/needs_profile) and a browser sign-in.
Two real DOM obstacles in the browser test: the v0.13.0 cookie-consent
banner intercepts clicks (added a reusable helpers/app.js
dismissCookieConsent), and a fresh OTC user redirects to the pending
"request is in review" page rather than staying on the capture-profile copy
(asserted the "Sign out" link + the review heading, dodging a strict-mode
double-match). 4/4 green, twice, ~1.7s.
Arc 6 — Ship + clean up
Committed 15af891, verified fast-forward safety against the unchanged
remote tip (a3f48ba), pushed to PR #4. Tore down Docker (down -v),
removed the worktree + throwaway venv. Shared rfc-app checkout never touched.
Cut state (end of session)
| rfc-app PR #4 | session-0046/e2e-tier1-scaffold @ 15af891 (fast-forward over scaffold a3f48ba). Open, not merged. Tier-1 suite green. |
rfc-app main |
unchanged @ 0c972c8 (v0.31.0) |
| OHM live | unchanged — deploys.id=46, v0.31.0, healthy |
| ohm-rfc pin | unchanged (v0.31.0) |
| flotilla | unchanged @ 5f1689f (v1.4.0) |
| Track Δ ledger | Status |
|---|---|
| #37 PPE pre-prod env | ⏸ deferred — own session (live infra; v1 gate already satisfied) |
| #38 e2e suite + gate | 🟡 Tier 1 shipped to PR #4 (green); Tier 2 gated on #37 |
| #39 blue/green slots | ⏸ depends on #37 |
| #40 compat gate | ⏸ Tier-1 harness is the next #38-adjacent piece |
| #41 canary routing | ⏸ depends on #39/#40 |
| #42 auto-rollback | ⏸ depends on #41 |
§19.2 / process candidates surfaced
- The e2e suite needs a stable test-posture profile. Three env knobs
(
SESSION_COOKIE_SECURE=false,RFC_APP_RATELIMIT_DISABLED=1,RFC_APP_INSECURE_WEBHOOKS=1) +TURNSTILE_REQUIRED=0are all "deterministic-test" toggles. PPE (#37) will want the same posture exceptSESSION_COOKIE_SECURE(https). Worth codifying as a named profile rather than scattered env when Tier 2 lands. - The (B) Gitea stub vs (A) real-Gitea split is now half-built. (A) is
the default everywhere today; (B) (the fast-inner-loop optimization)
remains a TODO behind
gitea.py. Decide whether (B) earns its keep once the suite has enough non-Gitea scenarios to make inner-loop speed bite. - flotilla multi-deployment audit (#37 dependency). The §8 deploy phases assume one host/project; driving a second deployment (PPE) on its own VM/gcloud-config is the real flotilla code change behind #37 — audit before standing up PPE.
What lands on the operator's plate
- Merge rfc-app PR #4 — the Tier-1 suite is green and one fast-forward
commit (
15af891) over the scaffold. Left unmerged because it's your PR from 0046 and the 0046 heads-up flagged a wedged Gitea merge-queue; merging is a clean operator gesture (additivee2e/+ a default-secureratelimit.pygate; no version, no deploy). When it merges, the ohm-rfc ROADMAP #38 row can be updated to "Tier 1 shipped." - Decide #37 PPE timing — it's unblocked (v1 shipped); it's a full
live-infra session (new GCP project + micro VM +
ppe.gitGitea + DNS + flotilla multi-deployment support). Confirm before any VM stand-up.
Prompt the operator can paste into the next Claude Code session
OHM driver session. Read ~/git/ohm-infra/SESSION-PROTOCOL.md and
~/projects/wiggleverse/ohm-rfc/ROADMAP.md (Phase F / Track Δ, #37–#42).
Claim your session id via claim-session-id.sh --start <now>.
State: flotilla v1 has shipped (v1.4.0) — the "gated on v1" condition on
Track Δ is ALREADY SATISFIED. Session 0047 shipped #38 **Tier 1**: a
working, green local-Docker e2e suite on rfc-app PR #4
(branch session-0046/e2e-tier1-scaffold, tip 15af891). `cd e2e && cp
.env.example .env && docker compose up -d --build && npx playwright test`
→ 4/4 green (health, landing, OTC API round-trip, OTC browser sign-in)
against a real disposable Gitea + Mailpit + fresh SQLite. A default-secure
RFC_APP_RATELIMIT_DISABLED gate was added to backend/app/ratelimit.py
(test-posture only; unit-tested). PR #4 is OPEN, not merged.
Pick one:
(a) Merge rfc-app PR #4 (operator gesture; additive, no deploy), then
update the ohm-rfc ROADMAP #38 row to "Tier 1 shipped."
(b) #38 follow-ons that need no VM: the (B) in-memory gitea.py stub
(fast inner loop), broader scenarios (propose-RFC / contribute-PR /
invites / notifications — reuse helpers/mailpit.js + helpers/app.js),
or the #40 Tier-1 compat-gate harness (N-1 image → migrate → N).
(c) #37 PPE — the big one. Unblocked but live-infra: a SEPARATE GCP
project + micro VM + colocated ppe.git Gitea + DNS + a real flotilla
multi-deployment code change (audit the §8 deploy phases first).
CONFIRM with the operator before standing up anything on a VM.
Heads-up (still relevant): a wedged Gitea merge-queue was seen in 0046
(persistent 405); verify git state against origin (git ls-remote) before
assuming a push/merge landed; put --max-time on curls; do hands-on git in
an isolated worktree (other sessions share the checkout).