317 lines
16 KiB
Markdown
317 lines
16 KiB
Markdown
# Session 0029.0 — Transcript
|
||
|
||
> Date: 2026-05-28
|
||
> Goal: Advance the OHM roadmap autonomously as a parallel driver session;
|
||
> pick one disjoint item that needs no operator input and doesn't collide
|
||
> with the two in-flight sessions.
|
||
>
|
||
> Outcome: **rfc-app v0.26.0 SHIPPED + DEPLOYED — roadmap #28 Part 1
|
||
> (auto-link accepted RFCs inside PR descriptions + PR/discussion
|
||
> comments). 363 backend tests green, frontend builds clean. Merged to
|
||
> main `698821f`, tagged v0.26.0 (origin + benstull), deployed to OHM as
|
||
> deploys.id=34 — live at ohm.wiggleverse.org/api/health = 0.26.0. The
|
||
> operator authorized merge + deploy mid-session.**
|
||
>
|
||
> Highlights:
|
||
> - Discovered the only buildable rfc-app feature (#28) and scoped it to
|
||
> its clean, non-colliding slice (Part 1).
|
||
> - Deliberate, documented spec deviation: read-time enrichment, not
|
||
> submit-time (§19.3-rule-2 note).
|
||
> - Zero migration (a concurrent session already holds migration 023);
|
||
> XSS-safe structured segments (no `dangerouslySetInnerHTML`).
|
||
|
||
---
|
||
|
||
## Pre-session state
|
||
|
||
- **OHM live**: rfc-app **v0.24.0** on the renamed VM `ohm-rfc-app`
|
||
(deploys.id stack from Session 0028: v0.22.0→v0.23.0→v0.24.0 landed
|
||
after the #1 VM-rename blocker cleared). Deploys unblocked, lock free.
|
||
- **ohm-rfc pin** (`.rfc-app-version`): `0.24.0`.
|
||
- **flotilla**: v1.2.0 built + pushed as a PR by Session 0028, NOT merged
|
||
(operator to open/merge/tag).
|
||
- **Active parallel sessions** at claim time (from `claim-session-id.sh`):
|
||
**0026** and **0027**, both claimed 2026-05-28T13-46, both `--INPROGRESS`.
|
||
- **rfc-app working dir** (`/Users/benstull/git/rfc-app`): a **shared**
|
||
checkout, mid-edit on branch `feature/v0.25.0-security-hardening`
|
||
(created 14:06 today off main/v0.24.0). Uncommitted, unpushed: OTC
|
||
verify-lockout, in-process rate limiting (`backend/app/ratelimit.py`),
|
||
HTML/XSS sanitization (`frontend/src/lib/sanitizeHtml.js` +
|
||
`MarkdownPreview.jsx` / `ProposalView.jsx` / `Editor.jsx`), and
|
||
**migration 023** (`023_otc_verify_lockout.sql`). This is one of the
|
||
0026/0027 sessions' rfc-app side — the v0.25.0 slot is claimed.
|
||
|
||
---
|
||
|
||
## Turn-by-turn arc
|
||
|
||
### Arc 1 — Claim, survey, and the parallel-session hazard
|
||
|
||
Claimed session ID **0029** via `claim-session-id.sh --start
|
||
2026-05-28T15-47` (placeholder pushed at `286e75f`). Read SESSION-PROTOCOL
|
||
§7; pulled ohm-rfc/ROADMAP.md and read it end to end.
|
||
|
||
Surveyed the candidate set from the launch prompt against "what can an
|
||
autonomous session actually ship tonight without operator input and
|
||
without colliding with 0026/0027":
|
||
|
||
- #21 Part A (Amplitude audit) — time-gated to ~2026-06-04 (needs a week
|
||
of real data). Not now.
|
||
- #22 (pro-consent copy) — blocked on operator-drafted + counsel-reviewed
|
||
text. Not now.
|
||
- #20 DMARC Phase B — time-gated to ~2026-06-04. #18/#20 ops remainder
|
||
(stale `wiggleverse/meta` webhook, bounce wiring) is operator/gitea-
|
||
admin work.
|
||
- #31b / #25-redesign — blocked on operator screenshots.
|
||
- #33 / #34 (name alignment) — operator-led, URL-breaking moves.
|
||
- **#28 (PR cross-references)** — Track B rfc-app feature, deps #3/#7/#12
|
||
all shipped. The one item buildable with no operator input. Picked.
|
||
|
||
Then the hazard surfaced: the real rfc-app checkout
|
||
(`/Users/benstull/git/rfc-app`) was a **shared working dir mid-edit** on
|
||
`feature/v0.25.0-security-hardening`. Per the launch prompt's explicit
|
||
warning ("branches alone do NOT isolate concurrent sessions sharing one
|
||
working dir — take a manual git worktree with its own venv"), I created
|
||
an isolated worktree:
|
||
|
||
```
|
||
git worktree add /Users/benstull/git/rfc-app-s0029 \
|
||
-b feature/v0.26.0-pr-rfc-links main
|
||
```
|
||
|
||
…and never touched the shared checkout again. The v0.25.0 slot being
|
||
claimed → I target **v0.26.0**.
|
||
|
||
### Arc 2 — Scoping #28 to its clean, non-colliding slice
|
||
|
||
#28 is explicitly "large, its own session" with three parts. I dispatched
|
||
one **read-only Explore agent** to map the PR/comment/RFC surfaces (folded
|
||
into this transcript per §5's driver-fold for read-only helpers — it did
|
||
no commits and warranted no separate `.1` transcript). Key findings:
|
||
|
||
- **PR descriptions and comments render as plain text** (`PRView.jsx:221`
|
||
description, `:443` PR conversation; `RFCDiscussionPanel.jsx:282`
|
||
discussion) — **not** through the `MarkdownPreview` surface the
|
||
security branch is hardening. **Zero frontend file overlap.**
|
||
- The security branch already created **migration 023**. Decisive signal
|
||
to make my slice **migration-free** — achievable by enriching at
|
||
read-time against the live `cached_rfcs` active set rather than
|
||
persisting links at submit time. That also avoids `main.py` (no new
|
||
endpoint) and any migration collision.
|
||
- RFC corpus: `SELECT slug, title, rfc_id FROM cached_rfcs WHERE
|
||
state='active'`. RFC view route is `/rfc/:slug`. Codebase convention
|
||
for internal nav inside these components is plain `<a href>` (e.g.
|
||
`PRView.jsx:190`), not react-router `<Link>`.
|
||
|
||
Scoped to **Part 1 only** (auto-link existing accepted RFCs), the
|
||
roadmap's recommended "easy win first". Parts 2 + 3 deferred.
|
||
|
||
### Arc 3 — Implementation (v0.26.0)
|
||
|
||
Read-time enrichment, migration-free, XSS-safe structured segments:
|
||
|
||
- **`backend/app/rfc_links.py`** (new): `build_index(conn, exclude_slug=)`
|
||
→ `LinkIndex.segment(text)` → list of `{type:"text"}` /
|
||
`{type:"rfc", slug, label, title}` segments. Conservative matching to
|
||
avoid false positives: links only **rfc_id** tokens (`RFC-0001`),
|
||
**multi-word** titles (whitespace present, e.g. `Open Human Model`), and
|
||
**hyphenated** slugs (e.g. `open-human-model`). A single common-word
|
||
title/slug (a hypothetical RFC titled "Human") is **not** linked — that
|
||
would turn every prose "human" into a link; the roadmap's "curated
|
||
canonical-terms list" stays an explicit future opt-in. Case-insensitive,
|
||
word-boundary-anchored, longest-match-wins, self-reference suppressed
|
||
via `exclude_slug`.
|
||
- **`api_prs.py`** `get_pr()`: builds the index once (exclude current
|
||
slug), adds `description_segments` and per-comment `text_segments`.
|
||
- **`api_discussion.py`** `get_discussion_thread_messages()`: per-comment
|
||
`text_segments`.
|
||
- **`frontend/src/components/LinkedText.jsx`** (new): maps segments onto
|
||
React text nodes + `<a class="rfc-autolink">`. No
|
||
`dangerouslySetInnerHTML` → XSS-safe by construction, independent of the
|
||
security branch's `sanitizeHtml.js`. Falls back to raw `text` when
|
||
segments are absent (older cached responses).
|
||
- Wired into `PRView.jsx` (description + PR conversation bodies) and
|
||
`RFCDiscussionPanel.jsx` (discussion bodies). `App.css`: a tokenized
|
||
`.rfc-autolink` rule (accent + dotted underline).
|
||
- `VERSION` → `0.26.0`; `frontend/package.json` → `0.26.0` (the
|
||
package-lock version field is stale at 0.21.0 across recent releases —
|
||
left as-is to match convention).
|
||
|
||
**Deliberate spec deviation** (recorded per §19.3-rule-2): the roadmap row
|
||
says the scan happens "at submit/post time"; I shipped **read-time**
|
||
enrichment. The intent the row actually names — "not as live compose
|
||
preview" — holds (drafts are never scanned). Read-time was chosen for
|
||
correctness (links track the live active set: newly-accepted RFCs start
|
||
linking, withdrawn ones stop), zero migration, and cheapness (the corpus
|
||
is small + cache-resident).
|
||
|
||
### Arc 4 — Tests, build, ship
|
||
|
||
- New `backend/tests/test_rfc_links_vertical.py`: 12 tests (9 scanner
|
||
units covering the gating rules, word boundaries, longest-match,
|
||
case-insensitivity, casing preservation; 3 end-to-end that PR
|
||
description + review comment + discussion comment all surface
|
||
`*_segments`, plus self-reference suppression). Two integration-test
|
||
fixes mid-flight: the change `original` text must exist verbatim in
|
||
`SEED_BODY` (else "stale"), and the PR description carries an appended
|
||
§6.5 `On-behalf-of:` trailer (so assert containment, not equality).
|
||
- **Full backend suite: 363 passed** (351 baseline + 12 new).
|
||
- **Frontend builds clean** (`npm ci` + `npm run build` with a throwaway
|
||
`.env` providing `VITE_APP_NAME`; the chunk-size warning is pre-existing).
|
||
- Committed `e794523`; pushed `feature/v0.26.0-pr-rfc-links` to **origin**
|
||
(git.wiggleverse.org) + **benstull** (mirror).
|
||
- ohm-rfc ROADMAP #28 row updated with a "Part 1 built + PR-pushed"
|
||
status note (commit `0f09403`, pushed to ohm-rfc main) so a future
|
||
driver doesn't re-pick Part 1.
|
||
|
||
I left the branch PR-ready and recorded a "built + PR-pushed" status note
|
||
on the ohm-rfc ROADMAP #28 row — initially deferring the merge/deploy to
|
||
the operator per the launch prompt's "coordinate the deploy lock + slot"
|
||
rule.
|
||
|
||
### Arc 5 — Operator authorized merge + deploy
|
||
|
||
Mid-session the operator replied: **"go ahead and merge the PR and
|
||
deploy."** So I carried it through, with two safety detours forced by the
|
||
parallel-session environment:
|
||
|
||
- **Merge.** `main` was not checked out in any worktree (the shared rfc-app
|
||
checkout holds the security branch with uncommitted work — untouchable).
|
||
Created a fresh detached worktree on `main`, `git merge --no-ff` the
|
||
feature branch (merge `698821f`), tagged `v0.26.0` (lightweight, house
|
||
style), pushed `main` + tag to origin + benstull. Version-slot note:
|
||
merging v0.26.0 makes the parallel v0.25.0 security session "last to
|
||
merge" — it renumbers/rebases per the roadmap rule; flagged for the
|
||
operator.
|
||
- **Deploy.** The canonical flotilla checkout had **uncommitted
|
||
`deploy.py` changes** — the 0026/0027 secret-redaction WIP on
|
||
`_PhaseRunner` — and its editable `.venv` runs the working tree. Rather
|
||
than actuate a production deploy with another session's unmerged code,
|
||
I deployed from a **clean detached worktree on flotilla `main`
|
||
(c26f251 = v1.1.0)** with its own venv. The state DB
|
||
(`~/.ohm-rfc-app-flotilla/`) is a fixed user path, so the worktree saw
|
||
the registered deployment + deploy log + §8.3 lock.
|
||
- **A scare, resolved.** A `deploy log | tail -12` appeared to show the
|
||
last *success* as v0.17.0 with three aborted v0.18.0 rows — seemingly
|
||
contradicting "live at v0.24.0." I stopped and probed ground truth:
|
||
`deploy status` + a direct curl both returned **0.24.0**. The "missing"
|
||
rows were a `tail` artifact (the log prints newest-first; `tail` hid
|
||
ids 26–34). No real discrepancy. (Lesson: probe live health before
|
||
trusting a truncated log view.)
|
||
- **Deploy.** Dry-run confirmed the plan (target v0.26.0, 7 secret refs
|
||
resolve incl. the already-bound `ANTHROPIC_API_KEY`, renamed VM
|
||
`/opt/ohm-rfc-app`, no migration step). Real deploy: all 9 phases ok,
|
||
**deploys.id=34**, health verify in 7.3s — no SSE-keepalive abort
|
||
despite deploying with pre-v1.2.0 flotilla (the intermittent failure
|
||
didn't trigger). Live `/api/health` = `0.26.0`; `pin check` matches.
|
||
- Updated the ohm-rfc ROADMAP #28 row to **Part 1 ✅ SHIPPED + DEPLOYED**
|
||
(commit `b4d39a8`). Removed all three temporary worktrees.
|
||
|
||
---
|
||
|
||
## Cut state (end of session)
|
||
|
||
| Repo / surface | State |
|
||
| --- | --- |
|
||
| rfc-app | `main` @ `698821f` (merge), tag **v0.26.0** pushed to origin + benstull. 363 tests green, FE builds. Feature commit `e794523`. |
|
||
| ohm-rfc | `main` @ `b4d39a8` — pin bumped to **0.26.0**, ROADMAP #28 Part 1 struck SHIPPED + DEPLOYED. |
|
||
| OHM live | **v0.26.0** on `ohm-rfc-app`, healthy (`/api/health`=0.26.0). **deploys.id=34** succeeded (9/9 phases, verify 7.3s). |
|
||
| Worktrees | All three temporary worktrees (rfc-app feature, rfc-app main-merge, flotilla deploy) removed. |
|
||
|
||
| #28 ledger | Status |
|
||
| --- | --- |
|
||
| #28 Part 1 (auto-link accepted RFCs) → v0.26.0 | ✅ shipped + deployed (deploys.id=34) |
|
||
| #28 Part 2 (offer-to-create-RFC) | ⏸ not started |
|
||
| #28 Part 3 (offer-to-contribute-to-pending) | ⏸ not started |
|
||
|
||
---
|
||
|
||
## §19.2 / process candidates surfaced
|
||
|
||
1. **Branch-chat auto-linking.** Part 1 enriches PR descriptions, PR
|
||
review comments, and PR-less discussion comments — but not branch-chat
|
||
messages (the AI-collaboration surface, `ChatPanel.jsx` +
|
||
`api_branches.py`). Extending `rfc_links` there is a small follow-up if
|
||
branch chat warrants it.
|
||
2. **Curated canonical-terms / per-RFC alias list.** The conservative
|
||
matcher deliberately skips single common-word titles/slugs. The roadmap
|
||
names a "curated canonical-terms list" for exactly these; it's left as
|
||
an explicit opt-in (config/content), not guessed at in framework code.
|
||
3. **rfc-app test deps aren't in `requirements.txt`.** `pytest` /
|
||
`httpx` / `pytest-asyncio` had to be pip-installed into the fresh
|
||
worktree venv by hand (the shared checkout's venv had them). A
|
||
`requirements-dev.txt` (or an extras group) would make a clean worktree
|
||
test-ready in one step. Minor ergonomic gap, like the `PYTHONPATH=.`
|
||
one CONTRIBUTING already notes.
|
||
|
||
---
|
||
|
||
## What lands on the operator's plate
|
||
|
||
1. **v0.26.0 is shipped + deployed (deploys.id=34) — nothing left here.**
|
||
Worth a real-world eyeball: open a PR or post a comment on OHM that
|
||
mentions an accepted RFC (RFC-0001 / "Open Human Model") and confirm
|
||
the inline auto-link renders to `/rfc/<slug>`.
|
||
2. **The parallel v0.25.0 security-hardening session (0026/0027) is now
|
||
"last to merge."** Because v0.26.0 landed first, that session must
|
||
rebase onto a `main` that's already at v0.26.0 and **renumber to
|
||
v0.27.0** (its target slot is taken). Expect a trivial top-of-file
|
||
CHANGELOG/VERSION resolution; the code regions are disjoint from
|
||
v0.26.0's (their MarkdownPreview/otc/ratelimit vs. my rfc_links/PRView/
|
||
discussion).
|
||
3. **The local flotilla `deploy.py` working tree still has the 0026/0027
|
||
secret-redaction WIP uncommitted** — I deployed from clean `main`
|
||
(v1.1.0) and left their WIP untouched. Nothing for you unless you want
|
||
to land it.
|
||
4. (Carried from 0028) flotilla v1.2.0 PR still open + unmerged — the
|
||
phase-7 SSE pre-drain it carries would have de-risked this deploy's
|
||
restart phase (which happened to succeed without it).
|
||
5. **Parts 2 + 3 of #28** remain unbuilt — the larger follow-up.
|
||
|
||
---
|
||
|
||
## Prompt the operator can paste into the next Claude Code session
|
||
|
||
```
|
||
OHM roadmap driver session, parallel-safe. Claim your ID first:
|
||
~/git/ohm-infra/scripts/claim-session-id.sh --start <now PST, YYYY-MM-DDTHH-MM>
|
||
It prints active --INPROGRESS sessions — coordinate. Read SESSION-PROTOCOL §7
|
||
+ pull ohm-rfc/ROADMAP.md.
|
||
|
||
STATE (end of 0029, 2026-05-28): OHM live at rfc-app **v0.26.0** on
|
||
ohm-rfc-app (deploys.id=34, healthy), pin=0.26.0, deploy lock free.
|
||
Session 0029 shipped + DEPLOYED #28 Part 1 (auto-link accepted RFCs in PR
|
||
text + comments). Still built-but-unmerged / in-flight:
|
||
- flotilla v1.2.0 (Session 0028) — PR open, not merged (its phase-7 SSE
|
||
pre-drain de-risks deploy restarts; the v0.26.0 deploy succeeded
|
||
without it).
|
||
- rfc-app v0.25.0 security-hardening (Sessions 0026/0027) — uncommitted
|
||
in the shared /Users/benstull/git/rfc-app checkout (+ uncommitted
|
||
deploy.py redaction WIP in the flotilla checkout). Because v0.26.0
|
||
merged first, this session is now "last to merge" → it must rebase
|
||
onto main@v0.26.0 and **renumber to v0.27.0** (slot 0.25.0 superseded).
|
||
Code regions are disjoint from v0.26.0's.
|
||
|
||
PICK ONE disjoint item. Open candidates: #28 Parts 2+3 (offer-to-create-RFC;
|
||
offer-to-contribute-to-pending — the larger follow-up, its own session, and
|
||
it will rebase on whatever v0.25.0's MarkdownPreview/sanitize work settled);
|
||
#21 Part A Amplitude audit (real data ~2026-06-04); #22 pro-consent copy
|
||
(operator-drafted + counsel text); #20/#18 ops (DMARC Phase B ~2026-06-04;
|
||
stale wiggleverse/meta webhook still pointing at deprovisioned
|
||
rfc.wiggleverse.org; bounce wiring); #31b/#25-redesign (operator
|
||
screenshots); #33/#34 repo + GCP name alignment (operator-led).
|
||
AVOID shipped/in-flight surfaces: #26/#27/#29/v0.21.0; the v0.25.0 security
|
||
files (otc.py, ratelimit.py, MarkdownPreview/ProposalView/Editor.jsx,
|
||
sanitizeHtml.js, migration 023); and v0.26.0's surfaces (rfc_links.py,
|
||
LinkedText.jsx, get_pr/discussion enrichment).
|
||
|
||
Hard rules: never ask for secret bytes (give the
|
||
`printf '%s' "$(pbpaste)" | .venv/bin/ohm-rfc-app-flotilla secret set
|
||
ohm-rfc-app <KEY>` gesture); coordinate the serialized deploy lock + version
|
||
slot with the operator; if you build in rfc-app, take a manual git worktree
|
||
with its own venv (the shared /Users/benstull/git/rfc-app checkout may be
|
||
mid-edit by another session); subagents write their own §5 subsession
|
||
transcripts; finalize + publish your transcript LAST (deliver the handoff
|
||
prompt before publish). Use branches + PRs. Claim your ID, then go.
|
||
```
|