add 0046/SESSION-0046.0-TRANSCRIPT-2026-05-30T06-28--2026-05-30T06-49.md + replace placeholder/variant SESSION-0046.0-TRANSCRIPT-2026-05-29T21-30--2026-05-30T06-19.md

This commit is contained in:
2026-05-30 06:52:57 -07:00
parent aed4cd0a48
commit a143b68d0a
2 changed files with 251 additions and 222 deletions
@@ -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 N1 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
N1-code-on-N-schema for the whole ramp. So the gate seeds the N1
baseline → applies N's migrations → runs the suite against N1 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 N1/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 + N1/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.
@@ -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 <sha>` /
`git show origin/main:<path>` (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
N1/N compat-gate steps were preserved and re-homed: they run **first in
Tier-1 Docker** (boot N1 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 <their commit>`
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
(1015 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 <now>.
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:<path>), put
--max-time on every curl, and check `git branch -r --contains <your
commit>` 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.
```