# Session 0033.0 — Transcript > Date: 2026-05-28 > Start: 2026-05-28T19-44 (PST implied) · End: 2026-05-28T20-14 > Goal: Advance the OHM roadmap by one disjoint item, parallel-safe. > Picked the recommended item: **#28 Parts 2 + 3** — offer-to-create-an-RFC > for strong-candidate terms, and offer-to-contribute-to-a-pending-RFC. > > Outcome: **rfc-app v0.29.0 shipped + deployed + verified live on > ohm.wiggleverse.org (deploys.id=40).** All three #28 parts are now live. > One additive migration (024, `contribution_requests`); no config / > overlay / secret / nginx / systemd change. 374 backend tests green (9 > new); frontend builds clean. Merged as PR #3 (`8a94e26`), tagged > `v0.29.0`, pushed origin + benstull mirror; ohm-rfc pin 0.28.0 → 0.29.0; > ROADMAP #28 row struck through (all three parts shipped). --- ## Launch prompt OHM roadmap driver session, parallel-safe. Claim ID first; read SESSION-PROTOCOL §7 + ohm-rfc/ROADMAP.md. State at hand-off (end of 0031): OHM at v0.27.0; flotilla v1.2.0 merged+tagged. Recommended item: **#28 Parts 2 + 3** → target v0.28.0 (possibly split). Avoid conflicting with session 0032 (then running) and v0.27.0's fresh security surfaces. Hard rules: never ask for secret bytes; fresh worktree+venv for rfc-app builds; coordinate the serialized deploy lock + version slot; FF-merge via gitea API; finalize+publish transcript LAST, handoff prompt before publish. --- ## Plan / TODO - [x] Claim session ID (→ 0033), read SESSION-PROTOCOL §7 + ROADMAP. - [x] Reconcile stale brief state vs reality (0032 finished; v0.28.0 shipped). - [x] Explore the #28 surfaces (scanner, propose flow, inbox, invite, roles, Haiku). - [x] Isolated rfc-app worktree + venv off main; baseline tests green. - [x] Part 2 — create-RFC offers (scanner candidate bucket + propose pre-fill + gating). - [x] Part 3 — contribute-to-pending (scanner pending bucket + request flow + inbox actions + accept→invite + decline). - [x] Tests (9 new), full backend suite, frontend build. - [x] CHANGELOG + VERSION + frontend mirror. - [x] Commit → PR #3 → FF-merge → tag v0.29.0 → push origin + benstull. - [x] ohm-rfc pin bump → deploy → verify live. - [x] ROADMAP #28 row struck through; worktree cleaned up. - [x] Finalize + publish transcript (this file). --- ## Pre-session state vs. reality (the brief was stale) The launch brief said OHM was live at **v0.27.0** and that session 0032 was *actively running*; it also implied v0.28.0 was un-tagged/un-deployed. By session start the picture had moved: - **0032 had finished** (transcript `…18-52--19-13`). Its work: IAP-only SSH hardening, flotilla v1.2.0 re-verify, orphaned-db cleanup, and rfc-app **v0.28.0** (audit I3 HTML-email guard + I4 async Turnstile) opened as PR #2. The only `--INPROGRESS` placeholders left were stale **0026 / 0027** (their security work already shipped as v0.27.0 — the brief said to ignore them). - The operator had since **merged, tagged, AND deployed v0.28.0**: live `/api/health` returned `version=0.28.0`, the ohm-rfc pin was `0.28.0`, and tag `v0.28.0` existed on main. This removed the version-slot ambiguity the brief worried about — there was no un-tagged predecessor to reconcile, so this session simply took the next slot, **v0.29.0**. Claimed session **0033** via `claim-session-id.sh --start 2026-05-28T19-44` (push to ohm-session-history is the atomic claim). --- ## Scope decisions (documented in code, CHANGELOG, and ROADMAP) Two interpretive calls shaped the build; both were made to match Part 1's conservative, false-positive-averse philosophy and were written into the code docstrings so they aren't re-litigated later: 1. **"Pending RFC" = a super-draft** (`cached_rfcs.state='super-draft'`), not a pre-merge idea PR. A super-draft is in `cached_rfcs` (so the #12 `rfc_invitations` FK the accept path reuses resolves), is owned (`owners_json` — routes the request, names the owner), and has a contribution surface to open. Pre-merge idea PRs satisfy none of those cleanly, so they're deliberately out of scope — a documented future extension. 2. **Part 2 candidate heuristic = multi-word #27-taxonomy tags with no defining RFC.** Multi-word is the same guard Part 1 uses for titles; a single common tag word (`identity`) would turn every prose occurrence into an affordance. The roadmap sanctions "a heuristic OR the Haiku path"; the cheap deterministic tag heuristic was chosen, with capitalized-phrase mining / repeated-term detection / the #27 Haiku (`ANTHROPIC_API_KEY`) pathway noted as sanctioned-but-deferred extensions (mirroring how Part 1 deferred the curated canonical-terms list). --- ## Turn-by-turn arc ### Arc 1 — Claim + orient Claimed 0033. Read SESSION-PROTOCOL §7, ROADMAP (#28 detail + operating instructions), and 0032's transcript. Confirmed live OHM = v0.28.0, pin = 0.28.0, deploy lock free, no competing active sessions. ### Arc 2 — Explore (two parallel Explore agents) Mapped the surfaces #28 Parts 2+3 touch: the role model (`require_contributor` gates propose; `permission_state='granted'` is "has create rights"); the RFC lifecycle (idea-PR-open → super-draft → active); the notification / §15 inbox system (`_emit_one` chokepoint, `render_summary`, kinds, the recipient-scoped inbox); and the #12 owner-invite flow (`rfc_invitations` + `rfc_collaborators`, accept writes the membership row). Verified migrations top out at 023 → **024 free**. ### Arc 3 — Backend build (isolated worktree + venv) Took a git worktree off main on branch `feature/v0.29.0-pr-rfc-create-contribute` with its own `.venv`; baseline 63 tests green. Then: - **`rfc_links.py`** rewritten to a three-bucket scanner (`Term` namedtuple + `kind` ∈ active/pending/candidate; precedence active > pending > candidate then longest-match-first). Kept backward-compat with the legacy `(key, slug, title)` 3-tuple so all Part 1 unit tests pass untouched. `build_index` now also gathers super-draft terms (with resolved owner display) and candidate tags. - **`notify.py`** — `fan_out_contribution_request` (one actionable notification per owner) + `notify_contribution_decided`; three new `render_summary` branches; the inbox list item now carries `extras` (recipient-scoped, so safe) for inline who/why/use-case. - **`api_invitations.py`** — extracted the invite issue path into one reusable `issue_invitation(...)` chokepoint shared by the manual endpoint and Part 3's accept. - **`api_contributions.py`** (new) — the GET target + request + accept + decline endpoints; accept fires `issue_invitation` (role contributor), reusing an existing pending invite if one exists. - **migration 024** — additive `contribution_requests` table (partial unique index = one open request per (RFC, user)). - Registered the new router in `api.py`. ### Arc 4 — Frontend build `LinkedText.jsx` renders the two new segment kinds as query-param-routed affordances (no prop-threading from deep in comment lists): `rfc-candidate` → `?propose=` (gated on create rights), `rfc-pending` → `?contribute= &term=` (gated on signed-in). `App.jsx` reads the params and opens `ProposeModal` (new `initialTitle`) / the new `ContributeRequestForm` (3-field). `Inbox.jsx` gained a `ContributionRequestRow` — the first actionable inbox kind — showing who/why/use-case with Accept/Decline. `api.js` + CSS additions. `viewer`/`canCreate` threaded into the three LinkedText call sites (PRView ×2, RFCDiscussionPanel). Frontend builds clean. ### Arc 5 — Tests 3 scanner-bucket unit tests + 6 end-to-end (PR-view segment surfacing; request→accept→invite→notify; decline; owner-cannot-self-request; active-RFC-rejected; eligibility). **Full backend suite 374 passing.** ### Arc 6 — Release + deploy VERSION + frontend mirror → 0.29.0; CHANGELOG entry with RFC-2119 upgrade steps. Committed; pushed branch; opened **PR #3** via gitea API; FF-merged (`8a94e26`); tagged `v0.29.0`; pushed tag + main to **origin and the benstull mirror** (the VM fetches rfc-app from the mirror — tag must be there before deploy). Bumped ohm-rfc pin 0.28.0 → 0.29.0, pushed. Ran `flotilla deploy ohm-rfc-app` — all 9 steps OK, **deploys.id=40**, verify 4.6s. Live checks: `/api/health` = `0.29.0`; the new `/api/rfcs/{slug}/contribution-target` route returns the handler's `{"detail":"RFC not found"}` (route wired); `GET /api/rfcs` still 200. ### Arc 7 — Record + cleanup ROADMAP #28 row struck through (all three parts shipped, with the deploys.id=40 + v0.29.0 link + the scope decisions); committed + pushed to ohm-rfc. Worktree removed; shared `/Users/benstull/git/rfc-app` checkout fast-forwarded to the merge. --- ## Friction / notes - The brief's pre-state was stale (OHM was already on 0.28.0, 0032 done). Verifying live state + tags first — rather than trusting the brief — avoided a phantom version-slot reconciliation. - No secret bytes were needed or requested (this release adds no secret/ config). Hard rule held trivially. - `issue_invitation` extraction is a small §19.2-style consolidation: the manual invite endpoint and the new accept path now share one chokepoint, so the dup-guard / token / email stay identical. --- ## Cut state (end of session) - **OHM live:** `https://ohm.wiggleverse.org/api/health` → `version=0.29.0`, `status=ok`. Deploy lock free. - **rfc-app:** `main` = `8a94e26` (PR #3 merge); tag `v0.29.0` on origin + benstull. 374 backend tests green. - **ohm-rfc:** pin `.rfc-app-version` = `0.29.0`; ROADMAP #28 struck through. `main` pushed (`62929b2`). - **deploys.id=40** records the v0.29.0 deploy. --- ## Operator-plate (manual gestures, if any) **None.** v0.29.0 needed no secret/config/overlay/nginx/systemd change; the additive migration 024 ran automatically during the deploy. Nothing is waiting on the operator.