diff --git a/0046/SESSION-0046.0-TRANSCRIPT-2026-05-29T21-30--2026-05-30T06-19.md b/0046/SESSION-0046.0-TRANSCRIPT-2026-05-29T21-30--2026-05-30T06-19.md deleted file mode 100644 index ce6be2f..0000000 --- a/0046/SESSION-0046.0-TRANSCRIPT-2026-05-29T21-30--2026-05-30T06-19.md +++ /dev/null @@ -1,222 +0,0 @@ -# Session 0046.0 — Transcript — PPE / progressive-delivery design, engineering handbook, ROADMAP Phase F - -- **Start:** 2026-05-29T21-30 *(approximate — the harness could not - recover the exact session-open time; the prior session's placeholder - 0045 is stamped 21-26, and this session's first git action was the - engineering-handbook seed commit at 2026-05-29 22:44 -0400)* -- **End:** 2026-05-30T06-19 -- **Operator:** Ben Stull -- **Driver:** Claude (claude-opus-4-8 via Claude Code) -- **Repos touched:** `wiggleverse/engineering` (created + seeded + §10.3 - update), `ben.stull/ohm-rfc` (ROADMAP Phase F via PR #3; two-tier - refinement via PRs #4 and #5), plus read-only clones of `rfc-app`, - `ohm-content` -- **Pin moves:** none — no rfc-app release, no `flotilla deploy` -- **TL;DR:** What began as "create a pre-prod env so we can run full - automated browser tests without screwing up production data" grew, in - conversation, into a full progressive-delivery design (PPE → automated - test gate → blue/green slots → weighted sticky canary → bake monitoring - → auto-rollback, all on the one existing VM with a shared forward-only - SQLite). That design was written up as a new org-level engineering - handbook (`wiggleverse/engineering`), and sliced into the OHM roadmap as - **Phase F / Track Δ, items #37–#42**. A late operator refinement — - two-tier testing (local Docker in the dev loop, then PPE on the VM) — - was folded into both. No code shipped; PPE itself is not built. - ---- - -## How the scope grew - -The opening ask was narrow: a pre-prod environment to run automated -browser tests without endangering production data. Over several exchanges -the operator widened it deliberately: - -1. First to a real environment with isolation guarantees. -2. Then: "plan for deployment to deploy to PPE, then run automated tests - (including browser automation for core scenarios), then do a blue/green - deployment and monitor real user behavior and roll back if there's any - sign of trouble." -3. Then a key architectural question: "Can we run all three instances - (ppe, blue, green) on a single VM? PPE would have its own database. - blue/green wouldn't but databases are roll-forward only. A portion of - users are sent to the newer version and the cohort increases as - confidence grows." -4. Then the insight that anchors the safety model: "Schema changes should - be backwards compatible with the last version of code though, right? - We should be testing that when we deploy to PPE." -5. "We'll also need infra for e2e tests in ppe. Specifically, email - accounts for invites, login, notifications." -6. And, late: "Could automated tests run against a local docker container - first during development and then on PPE on the VM when we're actually - deploying?" - -The driver's job became: pressure-test the design honestly, fill the -gaps, and write it down. - -## The design that settled (handbook §10) - -Verified against the real repos (after cloning `rfc-app`, which was not -initially on the machine): the OHM stack is a single GCE VM, **nginx** -reverse proxy, **systemd** units, a **Vite** frontend built on the VM, -and a single-file **SQLite** database (WAL). rfc-app migrations are -numbered and **forward-only** (`backend/migrations/001…024`, no -down-migrations) — exactly what makes the proposed canary safe. - -- **Three instances on one VM.** PPE (own DB, seedable/wipeable) + prod - **blue** and **green** code slots that *share* the one prod SQLite file - (WAL: many readers, one serialized writer). Isolation is by install dir - / systemd unit / DB path, not new hardware. Per-unit - `MemoryMax`/`CPUQuota` so a runaway slot (esp. the on-VM frontend build) - can't OOM the box. -- **The safety invariant** is stronger than "roll-forward only": it's - **expand/contract** (parallel-change). Because both slots hit the same - schema during a ramp, every migration must keep the *previous* code - version working (N schema must not break N−1 code). Additive changes are - safe; rename/drop/retype ship as expand-now / contract-next-release. -- **The gate tests the combination a normal deploy skips.** A vanilla - "deploy N and test" only exercises N-code-on-N-schema; the canary runs - N−1-code-on-N-schema for the whole ramp. So the gate seeds the N−1 - baseline → applies N's migrations → runs the suite against N−1 code on - the migrated DB (compat) *and* N code (forward), exercising writes both - directions. Fail compat ⇒ not canary-safe ⇒ block the ramp. -- **Canary, not binary flip.** nginx `split_clients` on a stable per-user - cookie → consistent slot assignment; ramp = edit % + reload. Rollback is - DB-free (weight bad slot to 0 + reload; schema only moved forward - compatibly). -- **Monitoring/rollback** trips on a *fast technical* signal (5xx / - health / latency), not product analytics. The genuine open fork: an - always-on watcher next to prod vs. a bounded bake during the deploy - session — in tension with flotilla's laptop-only commitment. Left as an - explicit OPEN decision, not defaulted. -- **Email infra for e2e.** A self-hosted **mail sink** (Mailpit). The - e2e suite reads OTC codes / invite links back via the sink's API. - Catches mail to any address, so multi-user invite scenarios need no - provisioned mailboxes. Deliverability (SPF/DKIM/DMARC) is kept separate - and non-gating (already roadmap #20). -- **Two tiers, one suite** (operator's final refinement). The e2e suite - is **environment-agnostic** — one `BASE_URL` (+ mail-sink URL). - **Tier 1**: a local `docker compose` stack (backend + built frontend + - fresh SQLite + Mailpit) run in the dev loop and in CI on every PR — - fast, offline, and **not dependent on #37**, so the suite can be green - locally before PPE on the VM exists. **Tier 2**: the *same* suite vs - PPE as the deploy gate (prod-like confirmation). The N−1/N compat gate - runs cheaply in Tier 1 (old image → migrate → test → new image), with - PPE as confirmation. The discipline: the suite is the unit of value; - each environment is just another `BASE_URL`. - -## Artifacts produced - -1. **`wiggleverse/engineering`** — a new org-level repo holding a - project-independent handbook, "How We Build Software at Wiggleverse" - (the repo's `README.md`, ~610 lines). Covers: vibe-code prototypes - first → write the first SPEC from the prototype → spec-driven - development → git/repo conventions → sessions & transcripts (linked to - the spec) → roadmap + parallel-agent sessions → the standard OHM stack - and how flotilla deploys it → three infra stages (Stage 1 pre-v1 - single-prod; Stage 2 v1+users PPE/blue-green/canary/rollback; Stage 3 - deferred, name-the-trigger). The progressive-delivery design is its - §10; the two-tier testing refinement is §10.3 (commit `0bbf759` on - `main`). Repo created empty by the operator; seeded by pushing the - guide straight to `main` (an empty repo has no base branch to PR - against), then default branch set to `main` and the leftover seed - branch deleted via the Gitea API. - -2. **`ohm-rfc/ROADMAP.md` Phase F (#37–#42)** — the design sliced into - roadmap items (item bodies + version-targets table + a new **Track Δ** - in the parallelization plan): - - #37 PPE pre-prod env (`ppe.ohm.wiggleverse.org`) — incl. the mail sink - - #38 Automated + browser E2E suite (rfc-app) + PPE test gate - - #39 Blue/green prod slots on the VM (slot model) - - #40 Forward-only migration phase + N−1/N compatibility gate - - #41 Weighted, sticky canary routing + promote/rollback - - #42 Bake monitoring + auto-rollback (open: watcher vs bounded bake) - All marked TBD and **gated on v1**, with handbook §10 as the binding - design and an explicit "proposal, not yet built" note (flotilla v1 - defers blue/green + auto-rollback per its SPEC §19.2). Landed via - **PR #3**. The two-tier-testing refinement was then folded in across - two follow-up PRs: **PR #4** (#40 body + #38 table row) and **PR #5** - (#38 body) — #38 Tier 1 explicitly does NOT depend on #37. Final - `ohm-rfc` `main` after all three: `2d6f6d6`. - -## Wrong turns (left in, per the honest-transcript rule) - -- **Harness output corruption — pervasive this session.** Bash stdout and - Read repeatedly returned garbled or *fabricated* content for larger - outputs — at one point inventing a plausible-but-fake Playwright config, - seed scripts, and Caddyfile for rfc-app *before that repo was even - cloned*; later injecting junk tokens and fabricated sentences into a - `git diff`, a Read result, a grep over this very transcript, and script - reads at finalize time. Several `Edit` calls also silently failed - (string-not-found, because a corrupted earlier Read had given the driver - slightly-wrong "current" text) — so a handbook §10.3 edit and a roadmap - #38-body edit each had to be retried after re-reading. Each time the - driver caught it, discarded the bad output, and re-verified via small - file-based commands and the public raw URLs. This transcript was written - wholesale via `Write` so its bytes did not depend on an unreadable - on-disk state. **This is the single most important caveat for the next - session: do not trust large tool dumps here; verify with minimal - commands and authoritative remotes.** - -- **"ohm-infra doesn't exist" — false, caused by the corruption.** Early - on, the driver concluded `~/git/ohm-infra/` (and the session tooling) - was absent on this machine, and even hand-built a throwaway - `~/projects/wiggleverse/ohm-session-history` clone to work around it. - That was wrong — `~/git/ohm-infra/` exists, with - `scripts/claim-session-id.sh`, `scripts/publish-transcript.sh`, and - `SESSION-PROTOCOL.md`, and is the canonical home for transcripts - (0043/0044/0045 present). This transcript was written to the correct - location and published via the real script. - -- **Roadmap item-numbering.** A corrupted early read of ROADMAP.md stopped - at item #17, so the driver first drafted the new items as #18–#23 — - which would have collided with the real, longer backlog (items run - through #36). The edits happened to fail (string-not-found), nothing - landed wrong, and Phase F was renumbered to the correct **#37–#42**. - -- **Wrong Gitea token.** First PR attempt used the GCP secret - `ohm-rfc-app-gitea-bot-token`, which authenticates as `ohm-bot` — *not* - a collaborator on `ben.stull/ohm-rfc` → 403 "user must be a - collaborator." Correct credential: the macOS **Keychain** PAT - (`security find-generic-password -s ohm-gitea-token`), Ben-owned, - `write:repository`. Memory corrected. - -- **Merge conflict (PR #3).** `origin/main` had advanced ~10 commits since - the branch base (the #36 meta-only settlement + pin bumps), so the PR - came up `mergeable:false`. Resolved by merging `origin/main` into the - feature branch (one ROADMAP.md conflict — kept the now-struck-through - #36 row from main, appended #37–#42), pushing, then merging. - -## Git operations (all verified against the public remotes) - -- `wiggleverse/engineering`: seeded to `main`; default branch set to - `main`; leftover `seed-engineering-handbook` deleted; §10.3 two-tier - section committed `0bbf759` and pushed (confirmed live on the public - raw README). -- `ben.stull/ohm-rfc`: Phase F via PR #3 → merge; two-tier refinement via - PR #4 (#40 + #38 table row) and PR #5 (#38 body); all three feature - branches deleted on remote; local `main` fast-forwarded to `2d6f6d6`; - tree clean. - -## Secrets discipline - -No secret bytes entered the conversation or any committed artifact. Tokens -used for API calls were read into `/tmp` files / shell vars, never echoed, -and the `/tmp` token files were truncated to zero at session end; only -env-var *names* and GCP secret *resource paths* were ever surfaced. - -## Not done / open - -- **PPE is not built.** This session only designed and recorded it. #37 is - the next concrete build, but its live-on-VM parts are **gated on v1** — - confirm timing with the operator. Note #38 Tier 1 (local Docker suite) - is NOT gated on #37 and can be started independently. -- **Open design decisions** in the roadmap: #42 auto-rollback model - (watcher vs bounded bake); #37 OAuth-per-host caveat (dissolves if #5 - email/OTC has shipped). -- **Session 0045 is unfinalized** (`SESSION-0045.0-TRANSCRIPT-…--INPROGRESS.md` - in `~/git/ohm-infra/`) — a prior pin-bump session's loose end, not this - session's. Flagged for cleanup; was unrelated to 0046's publish. -- **Stray read-only clone:** the driver made a - `~/projects/wiggleverse/ohm-session-history` clone while (wrongly) - believing the canonical tooling was absent; harmless and uncommitted, - can be removed. diff --git a/0046/SESSION-0046.0-TRANSCRIPT-2026-05-30T06-28--2026-05-30T06-49.md b/0046/SESSION-0046.0-TRANSCRIPT-2026-05-30T06-28--2026-05-30T06-49.md new file mode 100644 index 0000000..3f96e43 --- /dev/null +++ b/0046/SESSION-0046.0-TRANSCRIPT-2026-05-30T06-28--2026-05-30T06-49.md @@ -0,0 +1,251 @@ +# 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. +```