add 0025/SESSION-0025.0-TRANSCRIPT-2026-05-28T13-06--2026-05-28T13-30.md + replace placeholder/variant SESSION-0025.0-TRANSCRIPT-2026-05-28T13-06--INPROGRESS.md

This commit is contained in:
Ben Stull
2026-05-28 13:33:20 -07:00
parent bf4f9839da
commit 712f57e20a
2 changed files with 302 additions and 15 deletions
@@ -0,0 +1,302 @@
# Session 0025.0 — Transcript
> Date: 2026-05-28
> Goal: Pick one roadmap item disjoint from v0.21.0's polished surfaces
> and from #26/#29, build it on a manual worktree while deploys are
> frozen. Recommended fits from the 0022 handoff: #19 (CONTRIBUTING
> docs) or #27 (Haiku tag suggestions).
>
> Outcome: **#19 turned out to be already shipped (Session 0013.1) but
> never struck in the ROADMAP — which is exactly what misdirected this
> session to it. Pivoted to #27. Built Claude Haiku tag suggestions on
> the propose-RFC modal, tagged rfc-app [v0.24.0](https://git.wiggleverse.org/ben.stull/rfc-app/src/tag/v0.24.0),
> pushed to origin + benstull. 351 backend tests green, frontend builds
> clean. NOT deployed — deploys frozen behind the #1 VM-rename blocker
> (Session 0022); pin held at 0.22.0. ROADMAP bookkeeping corrected
> (#19 struck, #21 Part B noted shipped, #27 recorded as built+staged).**
>
> Highlights:
> - The §9.1 "Slice 2" AI-suggested tag chips, deferred since the
> propose modal first landed, are now wired.
> - Reused the existing §18 provider abstraction (`AnthropicProvider`,
> `_CLAUDE_VARIANTS["claude-haiku"]`) — added one small factory
> `providers.construct_haiku()`. `ANTHROPIC_API_KEY` was already a
> config var (the chat stack reads it), so #27's "new secret" may
> already be bound on OHM — flagged for the operator to check.
> - Held the pin and staged the deploy, consistent with how v0.23.0's
> pin is being held during the freeze.
---
## Pre-session state
| repo | sha | state |
| --- | --- | --- |
| rfc-app | `daebb54` | `main`; last tag v0.23.0 (#29). Backend 340 tests green. |
| ohm-rfc | `9f0c4e7` | `main` clean; pin `.rfc-app-version` = 0.22.0 |
| ohm-rfc-app-flotilla | `85f01f8` | session anchored here; `main` clean except untracked `.claude/` |
| OHM live | running v0.21.0 (per the 0019.0 / 0024.0 ledger), healthy | — |
**Deploy freeze in force.** The 0022 handoff: flotilla deploy of
v0.22.0 failed preflight (install dir `/opt/ohm-app` missing on the VM;
clean failure, no lock held, no partial deploy). Session 0022 is
investigating whether the #1 VM rename (`ohm-app``ohm-rfc-app`) was
started on the VM, leaving the §13 deployment record pointing at the
old path. Deploys blocked until 0022 clears it; OHM still healthy at
v0.21.0. Deploy order when it unblocks: v0.22.0 → v0.23.0 → mine.
Sessions 0022 (deploy-blocker, INPROGRESS), 0023, 0024 (roadmap-capture
#33/#34, done) preceded this. None were building #27.
---
## Turn-by-turn arc
### Arc 1 — Claim, orient, pick an item
Claimed session ID **0025** via `claim-session-id.sh --start
2026-05-28T13-06` (pushed the placeholder; the push is the atomic
claim). Read SESSION-PROTOCOL.md §7 + the session-start gesture, and
the full ROADMAP (PULL'd ohm-rfc first — already up to date).
Reasoned to **#19** over #27 initially: deploy-free (ships as PRs the
operator merges), zero code-surface collision with the avoid-list,
fully completable this session. #27 by contrast needs a new secret, a
counsel pass, and a deploy — none closeable during the freeze.
### Arc 2 — #19 is already done (the ledger lied)
Before touching anything, checked the repos for existing artifacts.
**Both `CONTRIBUTING.md` files already existed and were committed:**
- rfc-app `CONTRIBUTING.md` + SPEC §21 analytics chapter landed on
`main` via `213f686` (merge `ac3513a`, *"Session 0013.1"*) — the
commit message literally reads *"Lands roadmap item #19 ... and #21
Part B ... as a single docs commit."* Verified `213f686` is an
ancestor of `origin/main` and the file is in the `main` tree.
- ohm-rfc `CONTRIBUTING.md` committed (`90965d4` + later edits).
So **#19 + #21 Part B shipped back in Session 0013.1** and were simply
never struck in the ROADMAP version-target table. The 0022 handoff
recommended #19 not realizing it was done — the un-struck row is what
misdirected this session. This is the session's main friction finding
(see §19.2 candidates).
Pivoted to the other named fit, **#27**. Confirmed via local
transcripts + rfc-app tags/branches that no live session was building
it (0024 only *lists* #27 as a future candidate; no `v0.24.0` tag, no
`#27` branch).
### Arc 3 — Map the surfaces
Mapped before building (all reads, no subagents — #27 is one cohesive
feature, so the driver built it directly; no §5 subsession transcripts
warranted):
- **Existing LLM infra (§18 chat stack).** `backend/app/providers.py`
already has a multi-provider abstraction with `AnthropicProvider` and
`_CLAUDE_VARIANTS["claude-haiku"] = ("claude-haiku-4-5-20251001",
"Claude Haiku")`. `ANTHROPIC_API_KEY` is already a config var
(`config.py:44/91`) and `anthropic>=0.39` is already a dep. The
`models_resolver` docstring even anticipates *"the §9.1 tag
suggestions"*. So #27's "new secret" is the same key the chat stack
uses — it may already be bound on OHM.
- **Tags are free-form chip input** ("free-form for slice 1; the
AI-suggested chips of §9.1 are deferred to Slice 2"). No curated
taxonomy. So "the existing tag taxonomy" #27 references = the
de-facto set of distinct tags across `cached_rfcs` (`tags_json`).
- **Propose endpoint** is in `api.py`'s `make_router(config, gitea,
bot, providers)` closure, gated by `auth.require_contributor`.
- **Provider-call convention** (`api_prs._draft_with_provider`): async
endpoints call `provider.send()` directly (no threadpool), wrapped in
try/except → graceful stub fallback. Matched it.
- **Test harness**: no shared conftest; tests self-bootstrap via
`app_with_fake_gitea` / `sign_in_as` / `provision_user_row` from
`test_propose_vertical.py`. Runner `./.venv/bin/python -m pytest`
from `backend/` (340 tests pre-change).
### Arc 4 — Build #27 (worktree)
Manual worktree per the handoff (Agent worktree isolation is broken):
`git worktree add -b feature/v0.24.0-haiku-tags
/Users/benstull/git/rfc-app-v0.24.0-haiku-tags main`, then symlinked
`frontend/node_modules` + `backend/.venv` in. (`.gitignore` ignores
`.venv/`/`node_modules/` as directories; the symlinks showed as
untracked, so every commit staged explicit files — never `-A`.)
Backend:
- **`backend/app/tag_suggest.py`** (new) — `gather_tag_universe()`
(distinct corpus tags, most-common first, capped 200); `Draft`
dataclass; `haiku_provider(config)` seam (monkeypatchable; `None`
when no key); `build_messages` + tolerant `parse_reply` (drops
invented tags, dedupes case-insensitively to canonical spelling,
clamps confidence, caps); `suggest()` orchestration; in-process
per-user sliding-window rate limit (`TAG_SUGGEST_RATE_MAX` /
`_WINDOW_SECONDS`, defaults 30/60).
- **`providers.construct_haiku(api_key)`** — dedicated Haiku provider,
model id sourced from `_CLAUDE_VARIANTS`. Independent of
`ENABLED_MODELS`; no RFC slug at propose time so the §6.7 funder path
doesn't apply.
- **`POST /api/rfcs/suggest-tags`** in `api.py` — `require_contributor`,
rate-limit → 429, `SuggestTagsBody {title, pitch, use_case}`. Returns
`{suggestions:[{tag,confidence}]}`. Degrades to `[]` (200, never an
error) for no key / empty corpus / empty draft / parse failure.
- `backend/.env.example` — documented that `ANTHROPIC_API_KEY` also
powers tag suggestions + the two optional rate-limit vars.
Frontend:
- `ProposeModal.jsx` — debounced (700 ms, stale-response-guarded)
suggestion fetch on `[title, pitch, useCase]`; clickable suggestion
chips that add to the tag list (nothing auto-applies); the required
inline disclosure ("Suggestions are generated by Claude (Anthropic).
The text you've entered above is sent to Anthropic to produce them.").
- `api.suggestTags()` — forgiving; any non-OK → `[]`.
Tests: **`test_tag_suggest_vertical.py`** (11) — anon→401, contributor
gets universe-filtered suggestions (invented tag dropped), no-key→[],
empty-corpus→[] (model never called), rate-limit→429, plus units for
gather/parser-tolerance/max-cap/empty-draft-short-circuit/provider-
failure. One initial failure (the gather unit test needed the db
initialized, which happens in the TestClient lifespan) — fixed by
wrapping in a `TestClient(app)` context. Full suite **351 green**;
frontend `npm run build` clean (needed the deployment `.env` copied in
from the main checkout — gitignored, not committed).
### Arc 5 — Release (tag), staged deploy, bookkeeping
Followed the established release shape: feature commit on the branch
(`fb9b4fa`) → `--no-ff` merge to `main` (`376a6da`) → release commit
bumping `VERSION` + `frontend/package.json` + `CHANGELOG.md` (`28015ed`)
→ tag **`v0.24.0`** → pushed `main` + branch + tag to **origin** and
**benstull**. Re-ran the full suite on `main` post-merge: 351 green.
CHANGELOG `Upgrade steps:` (RFC 2119) names the **MUST**
`flotilla secret set ohm-rfc-app ANTHROPIC_API_KEY` gesture (stdin
pbpaste form; never paste bytes), notes the chat stack may already have
it bound, the optional rate-limit knobs, and a **SHOULD** counsel pass
on the disclosure wording (per #22).
**Pin deliberately NOT bumped** — held at 0.22.0, consistent with how
v0.23.0's pin is being held during the freeze. Bumping to 0.24.0 would
have jumped ahead of un-deployed 0.22.0/0.23.0 and scheduled a frozen
deploy. The deploy + pin bump are staged for the operator.
ROADMAP corrected + pushed (`f62cb90`): struck **#19** (with the
"misdirected 0025" note), annotated **#21 Part B** as shipped in
0013.1, recorded **#27** as built+tagged v0.24.0 with the full
operator-pending-gesture list. Did **not** touch #26/#29 rows — those
are tagged-but-not-struck pending deploy and belong to the still-open
Session 0022.
Removed the build worktree (branch preserved on both remotes).
---
## Cut state (end of session)
| | |
| --- | --- |
| rfc-app | `28015ed` `main`, tag **v0.24.0** (#27) — pushed origin + benstull. 351 tests green, frontend builds clean. |
| ohm-rfc | `f62cb90` `main` — ROADMAP corrected; **pin held at 0.22.0** |
| OHM live | unchanged — v0.21.0, healthy. **No deploy** (frozen). |
| flotilla | untouched |
| Roadmap ledger | Status |
| --- | --- |
| #19 CONTRIBUTING + onboarding | ✅ was already shipped (0013.1); struck this session |
| #21 Part B analytics discipline | ✅ was already shipped (0013.1) with #19; noted |
| #27 Haiku tag suggestions (v0.24.0) | 🟡 built + tagged + pushed; ⏸ deploy staged (freeze) |
---
## §19.2 candidates surfaced
1. **The ROADMAP version-target ledger lags reality and it cost a
session's worth of misdirection.** #19 + #21 Part B shipped in
0013.1 but were never struck; #26 (v0.22.0) and #29 (v0.23.0) are
tagged-but-not-struck pending deploy. A future session reading the
table can't tell "available" from "shipped-but-unbooked." Worth a
convention call: either (a) strike on *tag* (not deploy) with a
separate "deployed?" column, or (b) a fast `git tag` vs. table
reconciliation pass at session start. The first gesture of an
implementer session should be "is this already done?" — this session
only avoided wasted work by checking the tree before building.
2. **`ANTHROPIC_API_KEY` is overloaded across two surfaces** (the §18
chat stack and now #27 tag suggestions). Fine today (same provider),
but if a deployment wants a *cheaper/separate* key for the
high-volume suggestion path vs. interactive chat, that's a future
`TAG_SUGGEST_ANTHROPIC_KEY`-style split. Noted, not built.
3. **Tag suggestion quality is bounded by corpus size.** Constrained to
existing tags (per #27's explicit scope), a near-empty corpus yields
near-empty suggestions. The roadmap already names taxonomy-extension
(letting the model propose genuinely new tags) as the deferred
follow-up; this is the data point that it'll matter early for OHM.
---
## What lands on the operator's plate
1. **Clear the deploy freeze** (Session 0022's `/opt/ohm-app` blocker),
then deploy in order: v0.22.0 → v0.23.0 → **v0.24.0**, bumping the
ohm-rfc pin at each step and verifying.
2. **Before/at the v0.24.0 deploy:** set `ANTHROPIC_API_KEY` —
`printf '%s' "$(pbpaste)" | .venv/bin/ohm-rfc-app-flotilla secret set
ohm-rfc-app ANTHROPIC_API_KEY` (never paste the bytes). **Check
first** whether it's already bound for the §18 chat stack:
`flotilla secret list ohm-rfc-app`. Without it the app still serves;
suggestions are just unavailable.
3. **Counsel pass** on the propose-modal disclosure wording before
exposing the surface, per #22 discipline. The copy is honest as
written.
4. End-to-end verification of the suggestion surface happens
post-deploy (couldn't be done this session — no deploy, and no key
should ever touch the model).
---
## Prompt the operator can paste into the next Claude Code session
```
You are an OHM roadmap driver session. Claim your session ID first
(~/git/ohm-infra/scripts/claim-session-id.sh --start <now PST>); your
number is not assigned here. Read SESSION-PROTOCOL.md §7 + ohm-rfc/
ROADMAP.md (PULL ohm-rfc first).
State at handoff (from session 0025):
- rfc-app main @ 28015ed: v0.24.0 tagged + pushed (origin + benstull) —
roadmap #27, Claude Haiku tag suggestions on the propose-RFC modal.
351 backend tests green, frontend builds clean. NOT deployed.
- THREE rfc-app releases now sit un-deployed behind the freeze:
v0.22.0 (#26), v0.23.0 (#29), v0.24.0 (#27). ohm-rfc pin still 0.22.0
(all three pin-bumps held for deploy).
- ⛔ DEPLOYS STILL BLOCKED pending Session 0022's /opt/ohm-app VM-rename
investigation. Do NOT run flotilla deploy until that clears —
coordinate with the operator. Deploy order: v0.22.0 → v0.23.0 →
v0.24.0, bump pin + verify at each.
- #19 + #21 Part B were already shipped (Session 0013.1) — struck/noted
in the ROADMAP this session. The ledger had lagged; check the rfc-app
git tags against the version-target table before assuming a row is
available (#26/#29 are tagged-but-not-struck pending deploy).
Operator gestures pending for v0.24.0 (#27), at deploy time:
- Set ANTHROPIC_API_KEY (operator-run pbpaste|flotilla secret set; never
paste bytes). It may already be bound for the §18 chat stack — check
`flotilla secret list ohm-rfc-app` first.
- Counsel pass on the propose-modal disclosure copy (per #22).
Your job: pick one roadmap item that's shippable and disjoint from the
un-deployed surfaces (#26 ProposeModal/PRModal/RFCView/PRView,
#29 App.jsx sign-in region, #27 ProposeModal suggestion row +
tag_suggest.py + the suggest-tags endpoint) and from v0.21.0's polished
CSS. Build on a manual git worktree (git worktree add -b <branch>
<path> main; symlink frontend/node_modules + backend/.venv). Good
deploy-free fits while frozen: #21 Part A (Amplitude audit — but it
wants real-world data) or another Track-A surface that can build + tag +
stage. Avoid touching the three un-deployed features' code regions.
Hard rules: never ask for secret bytes; subagents write their own §5
subsession transcripts; finalize + publish your transcript last,
delivering the next handoff prompt to the operator before publish.
Claim your ID, then go.
```
@@ -1,15 +0,0 @@
# Session 0025.0 — Transcript
> Date: 2026-05-28
> Start: 2026-05-28T13-06 (PST implied)
> Status: **PLACEHOLDER — claimed at session start; finalized at session end.**
>
> This file reserves session ID 0025. The driver replaces this body
> with the full transcript before publishing, and renames the file to
> its final SESSION-0025.0-TRANSCRIPT-2026-05-28T13-06--<end>.md form.
---
## Launch prompt
_(launch prompt not captured at claim time)_