Files
session-history/ohm/0047/SESSION-0047.0-TRANSCRIPT-2026-05-30T11-02--2026-05-30T12-47.md
T

220 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 --build`
> stands 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-forward `a3f48ba..15af891`).
> One default-secure framework change rode along (`ratelimit.py` test
> 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:
branch `session-0046/e2e-tier1-scaffold` @ `a3f48ba`, an `e2e/` scaffold
(compose topology + helpers + `test.fixme` specs) — **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 from `e2e/`), plus
stale `--INPROGRESS` placeholders 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 so `parents[2]/VERSION` and the seed scripts' `parent.parent`
resolve as in prod; entrypoint reads the minted bot token into
`GITEA_BOT_TOKEN`. Same image runs the `gitea-seed` one-shot.
- `e2e/docker/frontend.Dockerfile` + `nginx.conf` — Vite build → nginx
serving the SPA + proxying `/api`,`/auth` to `backend:8000`.
- `scripts/bootstrap_e2e_gitea.py` — create the org, then reuse
`seed_meta_repo.py` to create + seed `META_REPO` (`ohm-content`).
- Rewrote `docker-compose.yml`: `gitea` (real `gitea/gitea:1.22`),
`gitea-init` (mint bot user+token via CLI → shared volume), `gitea-seed`
(org + seed), `backend`, `frontend`, `mailpit`, chained by `depends_on`.
- `.env.example`: added `SESSION_COOKIE_SECURE=false`, `GITEA_ADMIN_PASSWORD`,
annotated the runtime-overridden `GITEA_BOT_TOKEN`; root `.dockerignore`.
### Arc 4 — Bring it up; three real obstacles, fixed
Iterated against a live `docker compose up`:
1. **Gitea refuses to run as root** → every `gitea admin user create`
FATAL'd. Fix: container is root only to `chown` the fresh token volume;
the `gitea` commands drop to the `git` user via `su-exec`.
2. **gitea-init not idempotent** on an `up` over a persisted volume (user +
token-name `e2e` already existed → exit 1). Fix: short-circuit if the
token file exists; tolerate an existing user; unique token name.
3. **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-gated `RFC_APP_RATELIMIT_DISABLED` bypass in
`allow()` (mirrors `RFC_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`,
asserts `pending`/`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
1. **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=0` are all
"deterministic-test" toggles. PPE (#37) will want the same posture
*except* `SESSION_COOKIE_SECURE` (https). Worth codifying as a named
profile rather than scattered env when Tier 2 lands.
2. **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.
3. **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
1. **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 (additive `e2e/` + a default-secure
`ratelimit.py` gate; no version, no deploy). When it merges, the ohm-rfc
ROADMAP #38 row can be updated to "Tier 1 shipped."
2. **Decide #37 PPE timing** — it's unblocked (v1 shipped); it's a full
live-infra session (new GCP project + micro VM + `ppe.git` Gitea + 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).
```