# Session 0022.0 — Transcript

> Date: 2026-05-28
> Goal: Wave-11 driver — ship the two v0.21.0-disjoint roadmap items the
> operator picked (#26 + #29) via parallel worktrees; then, as the session
> evolved, ship a session-ID self-allocation **protocol amendment**, execute
> the **#1 VM rename** (`ohm-app` → `ohm-rfc-app`), and drain the deploy
> queue (incl. a concurrent session's v0.24.0/#27).
>
> Outcome: **Three rfc-app versions live on OHM — v0.22.0 (#26), v0.23.0
> (#29), v0.24.0 (#27, built by session 0025) — on a fully renamed
> `ohm-rfc-app` VM. Protocol amendment shipped: sessions now self-allocate
> their ID race-free via git, with in-progress visibility + checkpointing.
> OHM healthy at v0.24.0 throughout (one ~3-min planned downtime for the
> instance rename).**
>
> Subsessions: [0022.1](./SESSION-0022.1-TRANSCRIPT-2026-05-28T12-04--2026-05-28T12-28.md) (#26), [0022.2](./SESSION-0022.2-TRANSCRIPT-2026-05-28T12-04--2026-05-28T12-18.md) (#29).

---

## Plan (as it settled, with the operator)

- [x] Pick v0.21.0-disjoint Wave-11 items → operator chose **both** #26 (→v0.22.0) and #29 (→v0.23.0); parallel build, serial deploy, proceed autonomously.
- [x] Build via **manual** git worktrees (Agent `isolation:worktree` is broken) — one subagent per item, strict file ownership.
- [x] Integrate, tag, push both releases (origin + benstull); bump ohm-rfc pin.
- [x] **Protocol amendment** (operator request mid-session): session IDs self-allocated race-free via git; in-progress filename marker; checkpoint the transcript + a plan block.
- [x] **#1 VM rename** `ohm-app` → `ohm-rfc-app` (operator request) — full scope incl. GCP instance.
- [x] Deploy the queue: v0.22.0 → v0.23.0 → v0.24.0 (#27, session 0025).
- [x] ROADMAP strikes (#1, #26, #29); finalize + publish transcript.

---

## Pre-session state

- rfc-app `main` @ `2ac20b1`, VERSION 0.21.0; tags through v0.21.0. OHM live at v0.21.0 (deploys.id=29, aborted-but-healthy per the §19.2 SSE-watchdog note).
- ohm-rfc pin `0.21.0` @ `8a16cfa`. flotilla `main` @ `c8804a6`.
- Legacy `ohm-app` VM/user/dir/unit still in place (the #1 rename was an open candidate).
- Concurrency: prompt asserted "you are 0022"; sessions 0019/0020/0021 spoken for; a `SESSION-0024.0` already existed locally — the prompt-assigned-number scheme had visibly drifted (this seeded the protocol amendment).

---

## Turn-by-turn arc

### Arc 1 — Setup + item pick
Read SESSION-PROTOCOL.md + ROADMAP.md. Pulled all three repos. Confirmed deploy lock free (top row aborted, not in_progress) and no concurrent branch had claimed v0.22.0/#26/#29. Asked the operator: which item(s) + deploy gate. Operator chose **both #26 and #29**, parallel build / serial deploy, **proceed autonomously** through deploy.

### Arc 2 — Parallel build (subagents 0022.1, 0022.2)
`isolation:worktree` is broken (confirmed-known), so I created two **manual** worktrees off `main`, symlinked `frontend/node_modules` + `backend/.venv` into each (verified pytest collects 332), and dispatched two general-purpose subagents with strict file-ownership boundaries (no shared-file collisions; #29 owns App.jsx's non-header sign-in/route region, #26 owns the propose/RFC/PR components; migrations 021 vs 022). Both wrote their own §5 subsession transcripts before reporting.
- **0022.1 / #26**: found the framework's `rfcs`/PR surfaces are Gitea-backed **caches** rebuilt by the reconciler — so a cache column alone always reads NULL. Designed a canonical reconcile-proof side table `proposed_use_cases` (+ the literal cache columns for parity). 336 tests green.
- **0022.2 / #29**: `user_session_state` table; folded the read onto `/api/auth/me`; redirect gated on the Amplitude `identify` (#21 Part C ordering). 336 tests green.

Integrated #26 → main, bumped VERSION+package.json to 0.22.0, CHANGELOG, tagged v0.22.0, pushed origin+benstull. Rebased #29 on the new main (clean — disjoint api.py regions held), merged, tagged v0.23.0, pushed. 340 tests green with both. ohm-rfc pin → 0.22.0.

### Arc 3 — Deploy blocked, then a false alarm (the rabbit hole)
First `flotilla deploy` died on **expired GCP ADC** (`Reauthentication is needed`) — failed before the lock, no harm. Operator refreshed ADC (`gcloud auth application-default login`). Retried → **`preflight: install dir /opt/ohm-app missing on ohm-app`**. This sent me to inspect the VM, where I found `/opt/ohm-app` **perfectly healthy** and the service running — nothing renamed. Root cause: at deploy time the **`gcloud` CLI** user creds were *still* expired (only ADC had been refreshed); flotilla's preflight runs `test -d` **via `gcloud compute ssh`**, and `deploy.py:355` treats *any* non-zero return as "dir missing" — so a 1-second `gcloud` reauth failure masqueraded as a catastrophic missing-install-dir. **§19.2 candidate.** Operator ran `gcloud auth login`; the false alarm cleared.

### Arc 4 — Protocol amendment: session-ID self-allocation
Operator request: stop relying on the prompt's session number; claim it race-free via git, push a placeholder at start, finalize in place. Built `~/git/ohm-infra/scripts/claim-session-id.sh` (clones the public repo, computes `max(NNNN)+1` never filling gaps, pushes a placeholder — push *is* the atomic claim, with resync-and-retry on rejection; PR fallback documented). Extended `publish-transcript.sh` (accept `INPROGRESS` end token; replace same-`NNNN.M` variants in place at finalize). Amended `SESSION-PROTOCOL.md` (§7 + session-start gesture + §4 note) and flotilla `CLAUDE.md` (commit `85f01f8`). Claimed **0022** with the new script (validated the real push path). Later, per a follow-up: the claim script now **lists active `--INPROGRESS` sessions** so a starting session sees what's in flight; documented the **checkpoint convention** (a `## Plan` block + re-publishing the in-progress transcript at each checkpoint) and the **hooks** automation path (`SessionStart`/`Stop`/`SessionEnd`), with the honest finding that Claude has **no** built-in cross-session coordination — the shared-repo scheme is the right bespoke fill; hooks only *trigger* it.

### Arc 5 — #1 VM rename `ohm-app` → `ohm-rfc-app`
Operator: "we want the dir and service to be ohm-rfc-app" → chose the **full** rename incl. the GCP instance. Verified the external IP is a **reserved static** (`ohm-app-ip`/136.116.40.66) → instance stop/start is DNS-safe. Mapped every dependency: install dir (= service-user home), unit, user/group, nginx `root`, the venv's absolute-path shebangs, and the `DATABASE_PATH`/`PHILOSOPHY_PATH` overlays (+ live `.env`) — the production DB lives at `/opt/ohm-app/backend/data/ohm-app.db`.
- **Stage 1 (in-VM, instance running)**: stop service → `mv` dir → rename user/group → repair venv shebangs + `.env` paths in place → new `ohm-rfc-app.service` → nginx `root` → reload. Verified healthy (`v0.21.0 ok`) under the new layout.
- **Stage 2 (instance)**: `stop` → `set-name ohm-rfc-app` → `start`. Internal + reserved external IP preserved. Polled back to healthy.
- **Stage 3 (flotilla)**: surgical in-place `UPDATE` of the 4 `target_vm_*` fields in the state DB (no `deployment update` verb exists → §19.2; backed the DB up first; kept name `ohm-rfc-app` so overlay/secret rows stayed linked); repointed `DATABASE_PATH`/`PHILOSOPHY_PATH` overlays; aligned `scripts/register-ohm.sh` (flotilla `c26f251`).
- Deployed **v0.22.0** (deploys.id=31) onto the renamed layout — all 9 phases clean, **no SSE-watchdog recurrence**. Confirmed migrations 021/022 applied on the production DB and the new tables/column exist — **no data loss** (DB moved with the dir). Then **v0.23.0** (deploys.id=32).

### Arc 6 — Drain the queue: v0.24.0 (#27)
Operator: "the deploy queue is v0.22.0 → v0.23.0 → v0.24.0." v0.24.0 = **#27 Haiku tag suggestions** (built by concurrent session 0025). Verified the required `ANTHROPIC_API_KEY` was already bound (Secret Manager version 1, enabled) — **no secret-byte gesture needed**, and #27 degrades to silence if absent. Bumped pin → 0.24.0, deployed (deploys.id=33), health `0.24.0`.

### Arc 7 — Close-out
Struck #1/#26/#29 in the ROADMAP (ohm-rfc `ab351c5`). Delivered the next-session handoff prompt to the operator in chat (before publish, per §3). Published this transcript + the two subsession transcripts — the finalize **validated the §7 replace-in-place in production**: publishing `…--2026-05-28T13-53.md` git-rm'd the `…--INPROGRESS.md` placeholder in one commit (`14b241c`), leaving exactly one driver transcript for 0022. Removed the two rfc-app worktrees + their merged feature branches and the stale local placeholder.

### Arc 8 — Final re-publish
Operator: "update your transcript and publish and we're done." Updated this transcript to record arcs 7–8 and re-published (idempotent content-update on the same filename). The operator re-confirmed close-out; this transcript is the final, published record of Session 0022.0. **Session closed.**

---

## Cut state (end of session)

| | |
| --- | --- |
| rfc-app | `main` @ v0.24.0; tags v0.22.0 (`7d8371d`), v0.23.0 (`daebb54`) pushed origin+benstull (v0.24.0 by session 0025) |
| ohm-rfc | pin `0.24.0`; ROADMAP `ab351c5` (struck #1/#26/#29) |
| flotilla | `main` @ `c26f251` (CLAUDE.md `85f01f8`, register-ohm.sh `c26f251`); §13 record → ohm-rfc-app; no flotilla code change |
| OHM live | **v0.24.0, healthy** (deploys.id=33) on renamed VM `ohm-rfc-app` |
| VM | instance/user/dir(`/opt/ohm-rfc-app`)/unit(`ohm-rfc-app.service`) renamed; static IP preserved |
| ohm-infra | `claim-session-id.sh` (new), `publish-transcript.sh` (extended), `SESSION-PROTOCOL.md` §7 (new) — not a git repo; saved in place |

| Wave 11 ledger | Status |
| --- | --- |
| #26 use-case field (v0.22.0, id=31) | ✅ shipped |
| #29 sign-in resume (v0.23.0, id=32) | ✅ shipped |
| #27 Haiku tags (v0.24.0, id=33; built by 0025) | ✅ deployed by 0022 |
| #1 VM rename | ✅ shipped (ops) |
| Session-ID self-allocation amendment | ✅ shipped |

---

## §19.2 candidates surfaced

1. **flotilla preflight conflates SSH/auth failure with "install dir missing."** `deploy.py:355` raises the alarming `install dir … missing (fresh-VM provisioning is out of v1)` on *any* non-zero `ssh_runner` return — including exit-255 connection/auth failures. Cost me a VM-rename rabbit hole. Fix: distinguish `SshError`/255 (connection) from a `test -d` return of 1 (genuinely missing). Bundles with the long-standing **`flotilla deploy reconcile`** gesture + **SSE pre-drain** (v1.2.0 candidate). Note: the SSE watchdog did **not** recur across deploys.id=31/32/33.
2. **No `deployment update` verb.** Updating the §13 record after the VM rename required a direct SQLite `UPDATE` (state DB backed up first). A `deployment update <name> --vm-name … --vm-install-dir …` would make §13 edits a first-class gesture instead of hand-editing state.
3. **Cosmetic rename leftovers.** The reserved address is still named `ohm-app-ip` and the DB file is still `ohm-app.db` (both only console/path labels; renaming the address needs release+recreate, the DB file needs another move — neither worth the risk now). Flagged for completeness.

---

## What lands on the operator's plate

1. **Hooks (opt-in).** Offered to wire `SessionStart`/`Stop`/`SessionEnd` into `.claude/settings.json` to automate claim + checkpoint + finalize. Caveat: unconditional `SessionStart` claim would push spurious IDs for trivial read-only sessions — gate it or accept empty-but-finalized slots. Left as documented opt-in pending your call.
2. **sessions.json title for 0022** (publish adds an empty title) — and the legacy-prefix/empty-title cleanup pass noted in earlier handoffs.
3. **§19.2 #1 above** — a flotilla v1.2.0 (preflight error-classing + deploy-reconcile + SSE pre-drain) is now well justified.
4. Standing ops: DMARC Phase B (~2026-06-04, ≥1wk clean rua), stale `wiggleverse/meta` hook, #21 Part A Amplitude audit (~2026-06-04), #22 pro-consent copy (operator-drafted + counsel).

---

## Prompt the operator can paste into the next Claude Code session

```
You are an OHM roadmap driver session, running in parallel with others.
CLAIM YOUR SESSION ID FIRST (your number is not assigned here):

  ~/git/ohm-infra/scripts/claim-session-id.sh --start <now, YYYY-MM-DDTHH-MM PST>

It computes the next-free NNNN from the published wiggleverse/ohm-session-history
repo, pushes a placeholder transcript (atomic race-safe claim), PRINTS the
active in-progress sessions so you can see what else is running, and writes a
local placeholder you finalize at session end. Keep a `## Plan` block at the
top of that transcript and re-publish it at each checkpoint. Subagents are
NNNN.1, NNNN.2, …. Read ~/git/ohm-infra/SESSION-PROTOCOL.md §7 + ohm-rfc/
ROADMAP.md (PULL first) once at start.

STATE (end of session 0022, 2026-05-28):
- OHM live at rfc-app v0.24.0 on the RENAMED VM `ohm-rfc-app` (instance/user/
  dir /opt/ohm-rfc-app/unit ohm-rfc-app.service; static IP preserved). #1 VM
  rename is DONE. Deploys are unblocked and the lock is free.
- Shipped this session: v0.22.0 (#26 propose use-case field), v0.23.0 (#29
  sign-in state resume), and the deploy of v0.24.0 (#27 Haiku tags, built by
  session 0025). Session-ID self-allocation protocol amendment is live.
- gcloud CLI + ADC are authed. ANTHROPIC_API_KEY is bound.

PICK ONE disjoint roadmap item and build it on a MANUAL git worktree (Agent
isolation:worktree is BROKEN — git worktree add + symlink frontend/node_modules
+ backend/.venv). Open candidates: #28 (PR cross-references — large, its own
session), #31b / #25-redesign (need operator screenshots), #20/#18 ops (DMARC
Phase B ~2026-06-04, stale meta hook, bounce wiring), #21 Part A Amplitude
audit (~2026-06-04), #22 pro-consent copy (needs operator-drafted text). A
strong infra candidate: flotilla v1.2.0 — fix the preflight that misreports an
SSH/auth failure as "install dir missing" (deploy.py:355), add a
`deployment update` verb, add `flotilla deploy reconcile` + SSE pre-drain
(§19.2 from this session).

AVOID colliding with shipped surfaces: #26's ProposeModal/PRModal/RFCView/
PRView/ProposalView + `proposed_use_cases`; #29's App.jsx sign-in/route hook +
`/api/auth/me` + lib/useLastState.js; #27's ProposeModal suggestion row +
suggest-tags endpoint; the v0.21.0 polish surfaces (tokens.css, App.css,
index.css, App.jsx header, Inbox.*, Docs*).

Hard rules: never ask for secret bytes (give the `pbpaste | flotilla secret set
ohm-rfc-app <KEY>` gesture); coordinate the serialized deploy lock + version
slot with the operator (check rfc-app VERSION on latest main, rebase, take the
next free slot); subagents write their own §5 subsession transcripts; finalize
+ publish your transcript LAST (deliver the handoff prompt before publish).

Claim your ID, then go. The session after you takes the next free ID.
```
