16 KiB
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, 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 factoryproviders.construct_haiku().ANTHROPIC_API_KEYwas 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 onmainvia213f686(mergeac3513a, "Session 0013.1") — the commit message literally reads "Lands roadmap item #19 ... and #21 Part B ... as a single docs commit." Verified213f686is an ancestor oforigin/mainand the file is in themaintree. - ohm-rfc
CONTRIBUTING.mdcommitted (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.pyalready has a multi-provider abstraction withAnthropicProviderand_CLAUDE_VARIANTS["claude-haiku"] = ("claude-haiku-4-5-20251001", "Claude Haiku").ANTHROPIC_API_KEYis already a config var (config.py:44/91) andanthropic>=0.39is already a dep. Themodels_resolverdocstring 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'smake_router(config, gitea, bot, providers)closure, gated byauth.require_contributor. - Provider-call convention (
api_prs._draft_with_provider): async endpoints callprovider.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_rowfromtest_propose_vertical.py. Runner./.venv/bin/python -m pytestfrombackend/(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);Draftdataclass;haiku_provider(config)seam (monkeypatchable;Nonewhen no key);build_messages+ tolerantparse_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 ofENABLED_MODELS; no RFC slug at propose time so the §6.7 funder path doesn't apply.POST /api/rfcs/suggest-tagsinapi.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 thatANTHROPIC_API_KEYalso 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).
Arc 6 — Publish-script bug, fixed in passing
Publishing this transcript first failed: publish-transcript.sh
mkdir -ps the NNNN/ folder early, then git rms the INPROGRESS
placeholder — and because the placeholder was the only file in
0025/, git pruned the now-empty dir from the worktree, so the
subsequent cp died with "No such file or directory." This bites any
session whose folder holds nothing but its own placeholder (i.e. most
non-subagent sessions). Fixed by re-ensuring mkdir -p "$(dirname "$DEST")" immediately before the cp. ~/git/ohm-infra is a plain
local working dir (no VCS — see #33), so the fix simply lives in the
script file; re-ran publish and it succeeded.
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
- 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 tagvs. 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. ANTHROPIC_API_KEYis 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 futureTAG_SUGGEST_ANTHROPIC_KEY-style split. Noted, not built.- 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
- Clear the deploy freeze (Session 0022's
/opt/ohm-appblocker), then deploy in order: v0.22.0 → v0.23.0 → v0.24.0, bumping the ohm-rfc pin at each step and verifying. - 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. - Counsel pass on the propose-modal disclosure wording before exposing the surface, per #22 discipline. The copy is honest as written.
- 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.