# Session 0046.0 — Transcript > Date: 2026-05-30 > Goal: Fold the operator's two-tier-testing refinement into the > published docs — engineering handbook §10.3 + ohm-rfc ROADMAP #38 > (note on #40) — then, if time, author the rfc-app Tier-1 local-Docker > e2e scaffold as #37/#38 groundwork (no VM work). > > Outcome: **Done. Handbook §10.3 rewritten to the two-tier model > (engineering PR #1, merged). ohm-rfc ROADMAP now carries the full > two-tier treatment — #38 table row + detailed narrative, #40 narrative > + table row (PR #4 core + PR #5 coherence, both merged). rfc-app > `e2e/` Tier-1 scaffold authored (PR #4, left OPEN for review). No > deploys, no tags, no pin bumps — docs + groundwork only.** > > Texture note: this session ran amid the harness-corruption / > parallel-execution conditions flagged at launch (the prior advisory > "0046" hit Bash/Read garbling). Concretely: ROADMAP commits appeared > on `main` authored at this session's start time, and my coherence > commit (`30326e3`) reached `main` via a PR I did not open (#5, > `roadmap/38-body-two-tier`) while my own PR (#6, same commit) sat > open and un-mergeable. Plus a wedged Gitea merge-queue window. Every > claim below was re-verified against committed blobs, not large dumps. --- ## Pre-session state - Claimed session **0046** via `claim-session-id.sh` (max+1; the prior advisory "0046" never pushed a claim). Active `--INPROGRESS` at start: 0026, 0027 (stale), **0045** (a genuinely in-flight parallel session). - **engineering** (`wiggleverse/engineering`): one commit (`4ae1d03` seed). §10.3 was PPE-gate-only — no two-tier / local-Docker content. The repo now exists (the memory's "repo not yet created" was stale); `README.md` is canonical, `~/projects/wiggleverse/ENGINEERING-GUIDE.md` is the now-superseded original draft. - **ohm-rfc** (`ben.stull/ohm-rfc`): Phase F / Track Δ (#37–#42) present (PR #3). At session start a commit `2e403dd` ("ROADMAP #38/#40: two-tier") was already on `main` via PR #4, authored 06:29 — i.e. the prior corrupted turn's captured work had in fact landed, contradicting the launch brief's "did NOT yet write into the published docs." It updated the **#38 table row** + **#40 narrative bullet** only. - **rfc-app** (`ben.stull/rfc-app`): v0.31.0 (meta-only topology). No checked-in e2e suite; `scripts/seed_*` only. Backend = FastAPI/uvicorn `app.main:app` :8000, env-driven (`config.py` requires Gitea + OAuth + SECRET_KEY + webhook secret); frontend = Vite; email via SMTP env. --- ## Plan - [x] Claim session id (first gesture). - [x] Read SESSION-PROTOCOL.md + ROADMAP Phase F / Track Δ. - [x] Handbook §10.3 → two-tier model (branch → PR → merge). - [x] ohm-rfc ROADMAP #38 + note on #40 (verify done / complete coherence via PR with Keychain PAT). - [x] rfc-app Tier-1 local-Docker + e2e skeleton scaffold (if time). - [x] Surface open decisions (#42, #37 OAuth, v1 timing) — not default. - [x] Update memory. - [x] Finalize + publish transcript LAST. --- ## Turn-by-turn arc ### Arc 1 — Orient + claim, detect the corruption Read the protocol + ROADMAP. Claimed 0046. Early on, two reads of the same `git log` disagreed (one showed `2e403dd` as HEAD, the next showed the PR-#4 merge `ff4ff67`) — the flagged corruption, live. Adopted a discipline for the rest of the session: verify against `git show ` / `git show origin/main:` (committed blobs), never trust a large or first-time dump. ### Arc 2 — Handbook §10.3 (engineering) Rewrote §10.3 from "The PPE gate tests the combination a normal deploy skips" → **"Two-tier testing: one suite, local Docker then PPE."** The suite is environment-agnostic (one `BASE_URL` + a mail-sink API URL); Tier 1 = local `docker compose` (backend + built frontend + fresh SQLite + Mailpit) in the dev loop + CI on every PR (needs no VM → not blocked on PPE); Tier 2 = the same suite vs PPE on the VM as the deploy gate. The N−1/N compat-gate steps were preserved and re-homed: they run **first in Tier-1 Docker** (boot N−1 image → migrate → test → swap N image → test), PPE as prod-like confirmation. Also nudged §10.6 (machinery list + build-order). **Kept §10.4/§10.5/§10.6 numbering stable** because ROADMAP references handbook sections by number (#38→§10.3, #40→§10.2–§10.3, #41→§10.4, #42→§10.5). Branch → PR #1 → merged (`1f3c8e7`, merge `4bab7a0`). ### Arc 3 — ohm-rfc ROADMAP coherence (#38 / #40) Verified PR #4 (`2e403dd`) had already landed the core (#38 table row + #40 narrative bullet), leaving two gaps: the **#38 detailed narrative** (### 38 block, still PPE-only) and the **#40 table row**. Filled both on branch `session-0046/two-tier-narrative-coherence` (commit `30326e3`): #38 narrative now has the environment-agnostic contract + Tier 1/Tier 2 + "Tier 1 depends on nothing; Tier 2 depends on #37"; #40 row notes the compat gate runs first in Tier-1 Docker. Opened **PR #6**. The Keychain PAT API path needed care: `ben/ohm-rfc` 307-redirects; canonical is `ben.stull/ohm-rfc`. A POST hung once (killed it; it had in fact created the PR — 409 on retry). Then **PR #6 would not merge**: persistent `HTTP 405 "Please try again later"` for >2 min across retries and a close/reopen, despite `mergeable: true` and no branch protection — Gitea's conflict-check queue wedged server-side (a lingering lock, likely aggravated by the hung request + concurrent-session load). **Resolution discovered at finalize:** `main` later already contained my exact commit `30326e3` — merged via **PR #5** (`roadmap/38-body-two-tier`, merge `5760b5d`), a PR I did not open. A parallel actor (session 0045, or a corrupted parallel execution of this one) re-merged my commit. PR #6 was thus redundant (empty diff vs base — the real reason for the 405); **closed it as superseded by #5.** Net: `main` (5760b5d) carries the complete two-tier ROADMAP treatment, coherent. ### Arc 4 — rfc-app Tier-1 e2e scaffold (#37/#38 groundwork) Read the real runtime shape (`config.py`, systemd unit, `vite.config.js` proxy, `email.py` SMTP env, OTC endpoints `POST /auth/otc/request` + `/auth/otc/verify`). **Key finding:** Tier 1 cannot be a trivial two-container compose — the backend has a hard **Gitea dependency** (reads `META_REPO` content via the Gitea API, bot writes, `config.py` requires Gitea + OAuth env). So a hermetic Tier 1 needs a disposable Gitea (real container + bootstrap) or a stubbed gitea client — a genuine #38 design decision, surfaced not defaulted. Also: auth in tests uses **OTC, not OAuth** (the #37 caveat dissolves because #5/OTC shipped in v0.7.0). Authored `e2e/` (9 files, additive, no existing code touched): `README.md` (the design + the Gitea-dependency decision + the #40 compat-gate mapping), `docker-compose.yml` (backend + built-frontend + fresh-SQLite tmpfs + Mailpit + disposable Gitea + a bootstrap init, all `down -v` disposable; Dockerfiles marked TODO(#38)), `.env.example` (OTC auth, SMTP→Mailpit, `RFC_APP_INSECURE_WEBHOOKS=1`), `playwright.config.js` (env-agnostic, reads `BASE_URL`), `tests/helpers/mailpit.js` (poll sink API → extract OTC code / link; catch-all addressing → no provisioned mailboxes), and two spec stubs (`smoke`, `otc-login`, `test.fixme`). **Not a release** — no VERSION/CHANGELOG/pin/deploy. Pushed via SSH; opened **PR #4 (rfc-app), left OPEN for review** (correct end state for a framework-repo scaffold; should not auto-merge). ### Arc 5 — surface decisions, memory, finalize Updated `project_engineering_handbook.md` memory (repo exists/canonical; §10.3 = two-tier; open items refreshed). Surfaced the open decisions (below). Wrote + published this transcript. --- ## Cut state (end of session) | repo | state | | --- | --- | | engineering | `main` @ `4bab7a0` (PR #1 merge of `1f3c8e7`) — §10.3 two-tier testing + §10.6 touch-up | | ohm-rfc | `main` @ `5760b5d` (PR #5 merge of `30326e3`, atop PR #4 `2e403dd`) — full two-tier #38 (table+narrative) + #40 (narrative+row). PR #6 closed (superseded). | | rfc-app | branch `session-0046/e2e-tier1-scaffold` @ `a5a66e5`, **PR #4 OPEN** — `e2e/` Tier-1 scaffold. No release. | | OHM live | **untouched** — no deploy this session (docs + groundwork). | | ohm-rfc pin | **unchanged** (`.rfc-app-version` not bumped). | | memory | `project_engineering_handbook.md` updated. | | Track Δ ledger | Status | | --- | --- | | Two-tier refinement → handbook §10.3 | ✅ shipped (eng PR #1) | | Two-tier refinement → ROADMAP #38/#40 | ✅ shipped (ohm-rfc PR #4 + #5) | | #37 PPE | ⏸ gated on v1 (no VM work done) | | #38 e2e suite | 🟡 Tier-1 scaffold opened (rfc-app PR #4, for review) | | #39–#42 | ⏸ not started (chained, post-v1) | --- ## §19.2 / process candidates surfaced 1. **Gitea merge-queue wedge.** Persistent `405 "Please try again later"` for >2 min, surviving close/reopen, with `mergeable:true`. The conflict-check worker appears to stick after a killed/hung API request under concurrent-session load. May affect ANY merge instance-wide during such a window — operator may need to restart Gitea / its queue. A client-side `--max-time` on every `curl` is now mandatory discipline (one hung request poisoned the queue and held a server lock). 2. **PR identity under parallelism.** Two PRs (#5, #6) carried the *same* commit sha into the same base; one merged, one became an empty-diff orphan. Sessions should check `git branch -r --contains ` before assuming their own PR is the merge path — content-addressing means another actor can land "your" commit under a different branch. 3. **`config.py` Gitea-hardness vs. hermetic Tier-1.** The required Gitea + OAuth env is the structural obstacle to a cheap local e2e. Worth a framework note on a test-mode seam (the (A) real-Gitea vs (B) stub decision) so #38 doesn't rediscover it cold. --- ## What lands on the operator's plate 1. **#42 auto-rollback model — GENUINELY OPEN, surfaced not defaulted.** (a) always-on VM-side watcher (relaxes flotilla's laptop-only model for one purpose) vs. (b) bounded bake during the operator's session (10–15 min post-cutover, no daemon). Needed before #42 is built (post-v1, after #41). Handbook §10.5 + ROADMAP #42 both flag it. 2. **#37 OAuth-per-host caveat — effectively RESOLVED, confirm.** Because #5 email/OTC shipped (v0.7.0), PPE + the e2e suite log in via OTC, so no per-host OAuth app is needed. The scaffold encodes OTC-only auth. Flagging in case you still want an OAuth path tested on PPE. 3. **v1 timing.** Track Δ's live PPE-on-VM parts are gated on flotilla v1. I stood up **nothing** on the VM (local scaffold only). Confirm v1 timing before any VM stand-up for #37. 4. **rfc-app PR #4 (e2e scaffold)** — review when ready; its README poses the Tier-1 **Gitea-dependency decision** (real disposable Gitea + bootstrap vs. stub the gitea client) that #38 must settle. 5. **Gitea merge-queue** may be wedged (see §19.2 #1) — a queue/Gitea restart may be warranted; could affect session 0045 too. --- ## 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) first. Claim your session id via claim-session-id.sh --start . Context from session 0046: the two-tier testing refinement is fully landed — engineering handbook §10.3 (eng PR #1) and ohm-rfc ROADMAP #38/#40 (ohm-rfc PR #4 + #5) both on main. The e2e suite is environment-agnostic (one BASE_URL + mail-sink API URL): Tier 1 local Docker per-PR (no VM), Tier 2 PPE as the deploy gate; the #40 compat gate runs first in Tier-1 Docker. A Tier-1 e2e scaffold is OPEN for review at rfc-app PR #4 (branch session-0046/e2e-tier1-scaffold, new e2e/ dir, NOT a release). Its README poses the one real #38 design decision to settle FIRST: how Tier 1 satisfies the backend's hard Gitea dependency — a real disposable Gitea container + bootstrap (high fidelity) vs. stubbing the gitea client (fast). Auth in tests is OTC, not OAuth (the #37 OAuth caveat dissolved once #5/OTC shipped). Possible next moves: - Decide the Tier-1 Gitea approach, then flesh out rfc-app PR #4 (Dockerfiles + gitea bootstrap + real selectors) toward a passing Tier-1 suite — this is #38 and does NOT need the VM. - #37 (PPE on the VM) + everything live in Track Δ is GATED ON flotilla v1 — confirm v1 timing with the operator before any VM work. - #42 auto-rollback has an OPEN model decision (always-on VM watcher vs bounded-bake-during-session) — surface it, don't default. Heads-up: session 0046 hit a wedged Gitea merge-queue (persistent 405 "please try again later") and harness Bash/Read corruption — verify git state against committed blobs (git show origin/main:), put --max-time on every curl, and check `git branch -r --contains ` before assuming your own PR is the merge path (0046 had its commit merged via someone else's PR). Session 0045 was also in flight. ```