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:
@@ -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.
|
|
||||||
@@ -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
|
||||||
|
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 <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
|
||||||
|
(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 <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.
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user