26 KiB
Session K — Transcript
Date: 2026-05-28 Goal: Execute Wave 4 of
ohm-rfc/ROADMAP.mdas the autonomous driver per the "Operating instructions for the next session" section. The operator is awake-ish but stepped away mid-session; the driver dispatches each shippable item per wave as a forked subagent in a single message, serializes the deploys, verifies each, and writes this transcript at end-of-session. Wave 4 targets: #7 Admin user-management page (Track C1, v0.9.0), #9 Trust device 30d (Track C2, v0.11.0), #10 CloudFlare Turnstile (Track C, v0.12.0 — required to pause on operator-providedCLOUDFLARE_TURNSTILE_SECRET).Outcome: Two of three deployed to OHM live; third tagged and paused for the operator gesture, as designed.
- #7 Admin user-management page shipped as rfc-app v0.9.0 to OHM live (
deploys.id=15, all 9 phases green,/api/healthreturns{"version":"0.9.0","status":"ok"},/admin/users200s).- #9 Trust device 30d shipped as rfc-app v0.11.0 to OHM live (
deploys.id=16, all 9 phases green,/api/healthreturns{"version":"0.11.0","status":"ok"}).- #10 CloudFlare Turnstile tagged as rfc-app v0.12.0 at
b3f1b15onorigin+benstull. The OHM deploy is paused —flotilla deploynot yet invoked — until the operator runs the two MUST gestures from the v0.12.0 CHANGELOG (see operator-plate §1 below).ohm-rfc/.rfc-app-versionstays at0.11.0for now; OHM continues to serve v0.11.0 with abuse defense off.Four new roadmap items captured mid-session from the operator (added to
ohm-rfc/ROADMAP.mdas items #16, #17, #18, #19 — see arc 5 below): admin-create user with role + invite email, repo naming/location alignment, SMTP relay + Gitea webhook hardening, and CONTRIBUTING guides for rfc-app + ohm-rfc that link to the public session transcripts.Session-letter correction logged: the driver's prior commits in this session (
64f5344,54cadd8onohm-rfc) labeled this as Session J. The operator corrected mid-session: Session J was the parallel docs-feature / v0.14.0 session that ran during Session I's wind-down; this driver session is Session K. The correction landed in commitcfc0c6aonohm-rfc(which fixed the two prior strikethrough rows). The mislabeled commits stay as-written — no force-push, no--amend— per session-protocol discipline.
Pre-session state
- rfc-app:
mainatde28272(Release 0.14.0).VERSION=0.14.0. Tags shipped through v0.14.0; the v0.x lineage so far isv0.2.0, .1, .2, .3, v0.3.0, v0.4.0, v0.5.0, v0.6.0, v0.7.0, v0.8.0, v0.10.0, v0.13.0, v0.14.0— notev0.9.0,v0.11.0, andv0.12.0were unclaimed because items #7, #9, #10 hadn't shipped yet. Last migration on disk:015_passcode.sql. - ohm-rfc:
mainatd6af91f(Pin rfc-app to 0.14.0)..rfc-app-version=0.14.0. - OHM live: serving
v0.14.0perhttps://ohm.wiggleverse.org/api/health.flotilla deploy log ohm-rfc-appshows last successful deploy asdeploys.id=14(v0.14.0, succeeded). Two precedingfailedrows (id=12, 13) were from the parallel docs-feature session retrying. - ohm-rfc-app-flotilla:
mainata1588c1(v1.0.1 + CLAUDE.md bake-in). No work needed in this repo for Wave 4. - ohm-infra: most recent transcript was Session I, published. The convention says the next letter is J, but the parallel docs-feature session that shipped v0.14.0 between Session I's wind-down and this session's start claimed J — the operator confirmed mid-session that this driver session is Session K rather than J.
- Driver brief: the operator's bedtime prompt (the one that
opens this session) said Wave 4 lineup is #7 v0.9.0 + #9 v0.11.0
- #10 v0.12.0, with #10 pausing on
CLOUDFLARE_TURNSTILE_SECRET. It also pre-allocated migration slots 016, 017, 018.
- #10 v0.12.0, with #10 pausing on
Turn-by-turn arc
The session moves through five arcs, in order.
Arc 1 — Reconnaissance + dispatch
The driver read ~/git/ohm-infra/SESSION-PROTOCOL.md (the
binding cross-repo session shape) and ohm-rfc/ROADMAP.md (the
product order). Confirmed pre-session state: rfc-app main at
de28272 (v0.14.0), ohm-rfc pin at 0.14.0, OHM live on v0.14.0,
last migration 015. The bedtime prompt's claim that "OHM is
currently serving v0.10.0" was stale — the parallel docs-feature
session shipped v0.14.0 and successfully deployed it as
deploys.id=14 before this session opened. Folded that into the
state model and proceeded.
The flotilla CLI is invoked as
/Users/benstull/projects/wiggleverse/ohm-rfc-app-flotilla/.venv/bin/ohm-rfc-app-flotilla
(the operator's transcript convention writes flotilla as
shorthand; there's no global flotilla binary).
Dispatched three subagents in a single Agent message with three
parallel tool uses, subagent_type: general-purpose, no
isolation: worktree (each subagent creates its own git worktree add in /Users/benstull/git/rfc-app per Session-I lesson —
the Agent tool's worktree isolation only covers the flotilla
CWD, not the rfc-app CWD subagents need). Each prompt was
self-contained: full roadmap text, target version, reserved
migration slot, descending-order CHANGELOG insertion point, the
hard constraints (do NOT tag, do NOT touch the ohm-rfc pin, do
NOT run flotilla deploy, no force-push, no --amend, no secret
bytes, no git config edits), and a structured reporting brief.
Arc 2 — Session κ ships #7 → v0.9.0
Subagent κ branched at feature/v0.9.0-admin-users off
origin/main. Built the /admin/users surface (state-filter
chips, permission badge, Grant / Revoke, sign-up reason
blockquote under pending rows, decided-by/decided-at audit trail).
Wired POST /api/admin/users/{id}/permission with
self-flip-refusal (422) and non-admin gate (403). Added the
new_beta_request notification event_kind (category
admin-actionable), fired by notify.fan_out_new_beta_request
on first complete capture submission. Widened GET /api/admin/users payload. Updated BetaPending copy to be
honest about cadence (no SLA invented).
No new migration. The v0.8.0 014_beta_access.sql already
provides every column the page consumes. Slot 016 stays reserved.
Allowlist decision: /admin/allowlist stays as a sibling
sub-tab, not merged. The two surfaces key on different things
(allowlist by email pre-sign-up, user list by user_id
post-sign-up).
Pushed feature/v0.9.0-admin-users at 7872b92 to origin +
benstull. Tests: 200 passed (10 new in
test_admin_users_vertical.py). Frontend build clean.
Driver integration: clean fast-forward of main onto 7872b92
(both v0.9.0 and v0.11.0 / v0.12.0 branched from de28272, but
v0.9.0 is the first to merge so it FFs trivially). Tagged
v0.9.0 on the FF tip; pushed main + tag to origin +
benstull. Bumped ohm-rfc/.rfc-app-version from 0.14.0 →
0.9.0 (the pin tracks the latest deployed framework version,
not the highest semver). Ran flotilla deploy ohm-rfc-app:
opened deploys.id=15, all 9 phases green, verify took 4.8s,
/api/health returned {"version":"0.9.0","status":"ok"}.
The §8.2 stale-lockfile fault that bit Session I's v0.5.0 deploy
did NOT recur — subagent κ ran npm install in frontend/ after
bumping package.json, the lockfile diff is in the commit, the
VM's git checkout had nothing to clobber.
Arc 3 — Session λ ships #9 → v0.11.0
Subagent λ branched at feature/v0.11.0-device-trust off
origin/main (same merge base as κ — de28272). Built the trust-
device gesture: checkbox on OTC + passcode verify steps, opaque
token + rfc_device_trust cookie (HttpOnly + Secure +
SameSite=Lax + 30-day Max-Age), POST /auth/device-trust/start
to re-establish session on returning visit, /settings/ notifications → Trusted devices revoke list with per-row +
revoke-all controls, three new endpoints (GET /api/auth/me/ devices, DELETE for one and all).
Migration 017_device_trust.sql: new device_trust table —
id, user_id (FK CASCADE), device_token_hash (bcrypt,
unique idx), created_at, expires_at (= created + 30 days),
user_agent, last_seen_at, revoked_at NULL. Secondary idx
on (user_id, revoked_at) for fast active-row lookup.
Security shape: raw token never logged or stored, only the
bcrypt hash; lookup walks the candidate set with
bcrypt.checkpw for constant-time per row. A handled detail:
on a miss, device_trust_start returns a hand-built
JSONResponse (not HTTPException) so the cookie-clear
Set-Cookie header rides on the 401 — FastAPI's exception path
drops set_cookie calls.
Pushed feature/v0.11.0-device-trust at abd3626. Tests: 204
passed (14 new in test_device_trust_vertical.py, covering
cookie shape, hash≠raw-token, expired/revoked/forged clears,
single + cross-user + revoke-all). Frontend build clean.
Driver integration was the first non-FF cherry-pick of the
session — both v0.9.0 and v0.11.0 branched from de28272, so
once v0.9.0 was on main at 7872b92, v0.11.0's commit had to be
cherry-picked. Conflicts: VERSION, frontend/package.json,
frontend/package-lock.json (all three on the version-bump
line; resolved by taking the v0.11.0 value), CHANGELOG.md
(auto-merge succeeded — v0.11.0's entry slotted between 0.13.0
and 0.10.0, v0.9.0's entry between 0.10.0 and 0.8.0, no
overlap), SPEC.md (auto-merge succeeded, both releases
appended to §19.2 candidates in separate spots),
backend/app/api.py (auto-merge succeeded). The post-cherry-
pick tree at 6fb68a9 ran 214 tests green (= 190 base + 10
κ + 14 λ); tagged v0.11.0, pushed main + tag to origin +
benstull. Bumped pin 0.9.0 → 0.11.0; ran flotilla deploy ohm-rfc-app: opened deploys.id=16, all 9 phases
green, verify 2.4s, /api/health returned
{"version":"0.11.0","status":"ok"}.
A snag worth naming: the shared backend .venv at
/Users/benstull/git/rfc-app/backend/.venv does NOT have
app/ on its import path by default — running pytest from
that directory requires PYTHONPATH=.. The subagents had
worked in their own worktrees (where they presumably set up
their own pytest config or used pyproject.toml adjacency);
the driver's verification pytest had to be run with
PYTHONPATH=. .venv/bin/pytest -q from backend/. Not a
release fault — just a re-discovery of the test-runner
ergonomic. §19.2 candidate: a backend/conftest.py or a
backend/pyproject.toml [tool.pytest.ini_options] pythonpath = ["."] that makes pytest invocable without
the PYTHONPATH rune.
Arc 4 — Session μ tags #10 → v0.12.0, deploy paused
Subagent μ branched at feature/v0.12.0-turnstile off
origin/main (also from de28272). Built the Turnstile path:
backend/app/turnstile.pywithVerifyOutcomeenum (ok,skipped,misconfigured,missing-token,failed,network).OtcRequestBody.turnstile_tokenfield;/auth/otc/requestcallsverify_tokenbefore bcrypt + SMTP so a failed challenge spends no rate budget.TurnstileWidget.jsxReact wrapper over the official CloudFlare JS API (dynamic script load, cleanup on unmount).- Widget rendered on email-entry step + on passcode-verify step
(for the "Use a code instead" fallback dispatch, which calls
the same
/auth/otc/requestendpoint).
No migration. Slot 018 stays reserved.
TURNSTILE_REQUIRED default: false. Rationale: the operator
may temporarily forget to set the secret; soft-fail-on-missing-
secret keeps /login working during the wave-pause. The CHANGELOG
MAY-step tells the operator to flip to true once they've
confirmed a real sign-in works end-to-end so a future config drift
fails loud (HTTP 500 "auth misconfigured") instead of silently
disabling abuse defense.
Pushed feature/v0.12.0-turnstile at f7228d2. Tests: 195 passed
(5 new in test_turnstile_vertical.py; httpx.post mocked at the
app.turnstile boundary). Frontend build clean with and without
VITE_TURNSTILE_SITE_KEY set.
Driver integration was the most-conflicted cherry-pick — v0.11.0
and v0.12.0 share the Login.jsx verify step (both wanted to
inject a child element there) and share SPEC.md §19.2 (v0.11.0
moved the "Device trust" candidate to "Settled" + added three new
candidates; v0.12.0 moved the "Cloudflare Turnstile" candidate to
"Settled"). Resolutions:
Login.jsx: keep both — v0.11.0's trust-device checkbox renders first, v0.12.0's<TurnstileWidget>renders after it. They are orthogonal UI elements on the passcode-verify step.SPEC.md§19.2: keep v0.11.0's "Settled" device-trust entry + all three new v0.11.0-surfaced candidates (cross-device session revocation, password-equivalent change invalidates trust, device-trust window tunables) + v0.12.0's "Settled" Turnstile entry. Dropped v0.11.0's old "still-open Turnstile" candidate text and v0.12.0's old "still-open Device trust" candidate text (both superseded by the Settled-in-this-release entries on the other side of the merge).CHANGELOG.md: the auto-merger combined-but-overlapped the v0.11.0 and v0.12.0 entries (each tried to slot between 0.13.0 and 0.10.0). Resolved by hand-assembling the file into strict descending order: 0.12.0 above 0.11.0 above 0.10.0. The two entries are unaffected internally; the merge was purely about their relative position.VERSION,frontend/package.json,frontend/package-lock.json: resolved to v0.12.0 (the target).
Post-cherry-pick tree at b3f1b15 ran 219 tests green (= 214
prior + 5 μ). Tagged v0.12.0; pushed main + tag to origin +
benstull.
Did NOT bump pin. Did NOT run flotilla deploy. The wave is
paused on the operator gesture spelled out in the v0.12.0
CHANGELOG MUST block:
flotilla secret set ohm-rfc-app CLOUDFLARE_TURNSTILE_SECRET
flotilla overlay set ohm-rfc-app VITE_TURNSTILE_SITE_KEY <site-key>
# Optional after end-to-end verification:
flotilla overlay set ohm-rfc-app TURNSTILE_REQUIRED true
Once the operator confirms both env values are set, they (or the
next driver session) bump the pin 0.11.0 → 0.12.0 and run
flotilla deploy ohm-rfc-app. With TURNSTILE_REQUIRED=false,
deploying without the secret is non-fatal — /login still serves
but abuse defense is silently off; the pause exists for rollout
correctness, not crash-prevention.
Arc 5 — Operator adds #16, #17, #18, #19 mid-session
While the driver was integrating v0.9.0, v0.11.0, and v0.12.0,
the operator sent four asynchronous roadmap-add messages.
Captured and added to ohm-rfc/ROADMAP.md:
- #16 Admin-create user + invite email (custom message).
rfc-app, Track C, depends on #5 + #7. The new affordance lives
on the v0.9.0
/admin/userssurface: admin types first/last/ email, picks a role, optionally adds a custom message text, clicks Create — the system sends an invite email with a claim link. Distinguishes from #12 (per-RFC contribution invite) by being platform-level access provisioning rather than per-RFC membership. - #17 Repo naming + location alignment across
git.wiggleverse.org+git.benstull.org. Track Ω, no rfc-app version. Inventory + propose + execute the moves (personal-namespacerfc-app→wiggleverse/, mirror convention, per-deployment-flotilla naming rule documentation), with tombstone redirects + cross-repo reference updates. Likely a multi-session arc. - #18 Secure the SMTP relay + (Gitea) webhook. Partly Track Ω (SMTP creds, TLS, SPF/DKIM/DMARC, ingress restriction, bounce surfacing), partly Track C (webhook signature verification, replay-resistance, ingress). One session to inventory + fix easy wins; follow-ups for anything that turns into a framework release.
- #19 CONTRIBUTING guides + transcript-linked onboarding.
Two
CONTRIBUTING.mdfiles (rfc-app + ohm-rfc) that point outside contributors at the public session transcripts atwiggleverse/ohm-session-historyas the authoritative development arc, plus the explicit boundary: contributors open PRs; the operator alone approves, merges, bumps the pin, sets secrets, and runsflotilla deploy. Operator's phrasing: "I'm the only one that gets to yolo."
All four added as prose sections after #14 and as rows in the
version-target table. Items #16 and #17 landed in commit
64f5344 on ohm-rfc (alongside the v0.9.0 pin bump); #18
landed in cfc0c6a (alongside the v0.12.0 status row); #19
landed in cce9fe3 as a standalone roadmap commit at the very
end of the session.
The mid-session correction — "Not session J — session K" — landed
in cfc0c6a too: it fixed the two prior strikethrough rows
(#7 v0.9.0 and #9 v0.11.0) to attribute Session K, not J.
The two prior commits with the wrong attribution (64f5344,
54cadd8) stay as written, with the correction documented in
this transcript per session-protocol §3 (no curation, no
amend-after-push).
Cut state (end of session)
| Repo | State |
|---|---|
| rfc-app | b3f1b15 tag v0.12.0 (latest). Tags pushed to origin + benstull through v0.14.0, v0.13.0, v0.12.0, v0.11.0, v0.10.0, v0.9.0, v0.8.0, v0.7.0, v0.6.0, v0.5.0, v0.4.0, v0.3.0, v0.2.x. CHANGELOG strict-descending; SPEC.md §19.2 carries five new candidates from this session (see below). |
| OHM live | deploys.id=16, v0.11.0, healthy. /api/health returns {"version":"0.11.0","status":"ok"}. /admin/users 200s. /settings/notifications 200s. v0.12.0 deploy is paused on operator secret. |
| ohm-rfc | cce9fe3. .rfc-app-version = 0.11.0. ROADMAP reflects #7 + #9 shipped, #10 tagged-but-paused, #16 + #17 + #18 + #19 captured. |
| ohm-rfc-app-flotilla | a1588c1 (unchanged — no flotilla work in Wave 4). |
| ohm-infra | this transcript at SESSION-K-TRANSCRIPT-2026-05-28T03-20--2026-05-28T03-55.md; publish pending. |
| Wave 4 ledger | Status |
|---|---|
| #7 Admin user-management page (v0.9.0) | ✅ shipped — deploys.id=15 |
| #9 Trust device 30d (v0.11.0) | ✅ shipped — deploys.id=16 |
| #10 CloudFlare Turnstile (v0.12.0) | 🟡 tagged at b3f1b15, deploy paused on operator gesture |
§19.2 candidates surfaced
Numbered list of architectural / process / spec deferrals this session uncovered.
-
Cross-device session revocation surface (v0.11.0).
/settings/notifications → Trusted devicesrevokes device-trust rows. It does NOT revoke active session cookies in another browser, or v0.10.0's passcode-failure counter, or future credential shapes. The natural next step is one "active sessions and devices" page that lists every credential authenticating as this user. Earns its session when a second cross-cutting concern lands — most likely WebAuthn / Yubikey support. -
Password-equivalent change invalidates device trust (v0.11.0). v0.11.0 intentionally leaves device-trust rows live across passcode set / change / clear. The argument is structural; the argument against is conventional. Earns its session on either a security-review finding or user feedback.
-
Device-trust window tunables via env (v0.11.0). The 30-day window is hard-coded as
TRUST_DURATION_DAYS = 30inbackend/app/device_trust.py. Surface asDEVICE_TRUST_DURATION_DAYSenv var when a deployment asks. -
Grant/revoke user-facing notification (v0.9.0). The
personal_change_affecting_meenum already exists; v0.9.0 doesn't fire it on grant/revoke. Small follow-up. -
Decline-with-reason on Revoke (v0.9.0). Free-text into
permission_events.detailsJSON; no schema change. Small follow-up. -
Allowlist deprecation (v0.9.0). The
/admin/allowlistsurface stays as a sibling sub-tab in v0.9.0; the question of whether to retire theallowed_emailstable once/admin/usershandles every pending-user case is deferred to post-v0.9.0 operator experience. -
backend pytest
PYTHONPATH=.rune (driver discovery during v0.11.0 integration). The sharedbackend/.venvdoesn't putapp/on the import path;pytestfrombackend/requiresPYTHONPATH=.. Abackend/conftest.pyor abackend/pyproject.toml[tool.pytest.ini_options]\npythonpath = ["."]fixes it. Trivial; flag for a future cleanup session.
(The §3-invariant-1 hygiene is unchanged: no secret bytes anywhere
in any of the commits or this transcript. CLOUDFLARE_TURNSTILE_SECRET
is referenced by name; its bytes live only in the operator's
hand.)
What lands on the operator's plate
-
Run the v0.12.0 wave-pause gesture and re-enter the wave. The CloudFlare Turnstile release is tagged but not deployed. The operator must:
/Users/benstull/projects/wiggleverse/ohm-rfc-app-flotilla/.venv/bin/ohm-rfc-app-flotilla \ secret set ohm-rfc-app CLOUDFLARE_TURNSTILE_SECRET /Users/benstull/projects/wiggleverse/ohm-rfc-app-flotilla/.venv/bin/ohm-rfc-app-flotilla \ overlay set ohm-rfc-app VITE_TURNSTILE_SITE_KEY <site-key>then (in
ohm-rfc/):echo 0.12.0 > .rfc-app-version git add .rfc-app-version git commit -m "Pin rfc-app 0.12.0 (Turnstile)" git push origin mainthen back in the flotilla CWD:
/Users/benstull/projects/wiggleverse/ohm-rfc-app-flotilla/.venv/bin/ohm-rfc-app-flotilla \ deploy ohm-rfc-appAfter verification with a real sign-in, flip the production- locked shape:
/Users/benstull/projects/wiggleverse/ohm-rfc-app-flotilla/.venv/bin/ohm-rfc-app-flotilla \ overlay set ohm-rfc-app TURNSTILE_REQUIRED trueThe next driver session will strikethrough
#10inohm-rfc/ROADMAP.mdonce the deploy completes successfully. -
Decide phasing for items #16, #17, #18, #19. Four new roadmap items captured this session; pick which wave each rides. #16 (admin-create user + invite) is naturally a Wave-5 candidate alongside #12. #17 (repo naming) is operator-led; the inventory step can be a Track-Ω subagent but the rename gestures are live moves. #18 (SMTP + webhook hardening) is similar — partly subagent-doable, partly operator-led. #19 (CONTRIBUTING guides) is a Track-Ω docs PR that can ride alongside any other item; a subagent can draft it and the operator approves + merges as a normal PR.
-
Cleanup: there are no leftover worktrees on
/Users/benstull/git/rfc-app— all three subagent worktrees were removed at the end of the session. -
§19.2-list follow-up. Seven candidates surfaced this session (see above). None blocks the next wave. The pytest-PYTHONPATH one is trivial enough to be folded into the next session's warm-up.
-
Session-letter correction documentation: this transcript records the J→K correction explicitly. If the parallel docs-feature session (the real Session J) hasn't yet published its transcript, that's its own session's responsibility — not this one's.
Prompt the operator can paste into the next Claude Code session
You are the OHM roadmap driver. The previous session (Session K)
shipped rfc-app v0.9.0 (admin user-management page, item #7,
deploys.id=15) and v0.11.0 (trust device 30d, item #9,
deploys.id=16) to OHM. v0.12.0 (CloudFlare Turnstile, item #10)
is tagged at `b3f1b15` on origin + benstull but the OHM deploy is
paused on `CLOUDFLARE_TURNSTILE_SECRET` + `VITE_TURNSTILE_SITE_KEY`
(see SESSION-K transcript §"operator plate"). OHM serves v0.11.0
until the operator clears the pause.
Four new roadmap items landed mid-Session-K from the operator:
- #16 admin-create user + invite email (with optional custom
message), rfc-app, depends on #5 + #7.
- #17 repo naming + location alignment across
`git.wiggleverse.org` + `git.benstull.org`, ohm-infra + cross-
repo, Track Ω.
- #18 secure the SMTP relay + Gitea webhook, partly ohm-infra,
partly rfc-app framework hardening.
- #19 CONTRIBUTING guides for rfc-app + ohm-rfc that link to the
public session transcripts at
`wiggleverse/ohm-session-history`, with the explicit boundary
that contributors open PRs and the operator alone bumps pins +
sets secrets + runs flotilla deploy.
Read `~/git/ohm-infra/SESSION-PROTOCOL.md` and
`/Users/benstull/projects/wiggleverse/ohm-rfc/ROADMAP.md`
end-to-end. Pick a path:
A. **Finish Wave 4** by clearing the v0.12.0 pause: if the
operator has set `CLOUDFLARE_TURNSTILE_SECRET` and
`VITE_TURNSTILE_SITE_KEY`, bump pin to 0.12.0 and deploy.
Verify `/api/health` returns `{"version":"0.12.0","status":"ok"}`
and that a `/login` page renders the Turnstile widget. Then
strikethrough `#10` in ROADMAP. Then ask the operator before
flipping `TURNSTILE_REQUIRED=true`.
B. **Start Wave 5** if Wave 4 is clear and the operator wants to
advance:
- Session ν (Track B): #12 Owner invite → v0.16.0 (slot 14 was
claimed by docs).
- Session ξ (Track A): #13 Amplitude → v0.15.0 (operator must
provide `AMPLITUDE_API_KEY` — same wave-pause pattern as #10).
- Optionally session ρ: #16 Admin-create user + invite → next
available slot. Lives on the v0.9.0 surface; subagent should
read `frontend/src/components/Admin.jsx` and
`backend/app/api_admin.py` for the create-user affordance,
plus `backend/app/email.py` for the invite-email template.
Wave-5 dispatch should reuse Session K's subagent pattern: each
subagent creates its own `git worktree add` in
`/Users/benstull/git/rfc-app`, pushes a feature branch, does NOT
tag, does NOT touch the ohm-rfc pin, does NOT run flotilla deploy
— the driver integrates serially. The cherry-pick conflicts are
all in the same usual places: `VERSION`, `frontend/package.json`,
`frontend/package-lock.json`, `CHANGELOG.md` (strict descending
order), `SPEC.md` §19.2, occasionally `Login.jsx`.
Session-K lessons to apply automatically:
- Subagents push feature branches only. Driver tags, bumps the
pin, deploys.
- Migration slots: 016 + 018 stayed reserved (no schema change
needed for #7 or #10). 017 was used by #9. Next free is 016 or
018 depending on what's next.
- CHANGELOG inserts go in version-descending order. After v0.12.0
the file is: 0.14.0 → 0.13.0 → 0.12.0 → 0.11.0 → 0.10.0 → 0.9.0
→ 0.8.0 → ... — subagents inserting a new entry slot it at the
right position; cherry-pick conflicts get hand-assembled if two
features ship at adjacent slots.
- backend pytest needs `PYTHONPATH=.` from `backend/` unless
someone fixes the §19.2 candidate.
- `Login.jsx` is now contested territory — two consecutive
releases (v0.11.0 + v0.12.0) edited the same component on the
verify step. Expect a conflict any time #16's admin-create-user
invite-claim flow shares the path.
- Item #1 VM rename and operator-provided secrets stay operator
territory.
End-of-session: write `~/git/ohm-infra/SESSION-L-TRANSCRIPT-…md`
and publish via `~/git/ohm-infra/scripts/publish-transcript.sh`.