diff --git a/SESSION-M-TRANSCRIPT-2026-05-28T05-26--2026-05-28T06-50.md b/SESSION-M-TRANSCRIPT-2026-05-28T05-26--2026-05-28T06-50.md new file mode 100644 index 0000000..e4311e1 --- /dev/null +++ b/SESSION-M-TRANSCRIPT-2026-05-28T05-26--2026-05-28T06-50.md @@ -0,0 +1,708 @@ +# Session M — Transcript + +> Date: 2026-05-28 +> Goal: Drive Wave 6 — three Track Ω inventory / draft subsessions in +> parallel. No rfc-app feature releases this wave; the wave's job is +> to surface the operator-led gestures that the post-Wave-5 plate +> needs (DNS edits, repo renames, counsel-reviewed copy) and to land +> docs that make OHM's contribution surface honest. +> +> Outcome: **All three subsessions produced their deliverables.** +> M.1 wrote docs feature branches in rfc-app + ohm-rfc (CONTRIBUTING +> + SPEC §21 analytics chapter) — pushed, awaiting operator PR +> review. M.2 wrote a 33 KB repo-naming inventory + move proposal +> with six open questions for the operator. M.3 wrote a deliverability +> + webhook hardening runbook + a rfc-app v0.18.0 framework-hygiene +> proposal — and surfaced one potentially-load-bearing finding: +> SMTP_HOST may be missing from the live flotilla overlay (every OHM +> email path silently broken if so). M.1 hit a stream-watchdog stall +> on the report-back step AFTER writing its transcript + pushing both +> feature branches — recovery was driver-reads-transcript-direct, no +> re-dispatch. +> +> First session running under the §5 subsession-transcript amendment +> (Session-L addition to SESSION-PROTOCOL.md). Three forked +> subagents pre-assigned M.1 / M.2 / M.3; each writes its own +> `SESSION-M.-TRANSCRIPT-…md` before returning a tight report to +> the driver. 13 §19.2 candidates surfaced across the three subs + +> driver. + +--- + +## Pre-session state + +**Time**: opened 2026-05-28T05-26 PST (Thursday, just-pre-dawn — +operator handed off the wheel after Session L's three-deploy +Wave 5 finish at 05:17 PST and is sleeping). Session L's main +transcript is at +`~/git/ohm-infra/SESSION-L-TRANSCRIPT-2026-05-28T04-22--2026-05-28T05-17.md`. + +**Repos at session open:** + +| Repo | State at open | +| --- | --- | +| `rfc-app` (`~/git/rfc-app`, branch `main`) | clean tree, HEAD at `1456c8b` (Release 0.17.0: admin-create user + invite email + #21 Part C). Tags through `v0.17.0`. | +| `ohm-rfc` (`~/projects/wiggleverse/ohm-rfc`, branch `main`) | clean tree, HEAD at `47cb2f9` (ROADMAP strike of #12 / #13 / #16). `.rfc-app-version` = `0.17.0`. | +| `ohm-rfc-app-flotilla` (`~/projects/wiggleverse/ohm-rfc-app-flotilla`, branch `main`) | clean tree, HEAD at `65c0e55` (CLAUDE.md: correct the secret-set gesture). v1.0.1. | +| `ohm-infra` (`~/git/ohm-infra/`) | Plain directory (not a local git repo). Transcripts A–L published to `wiggleverse/ohm-session-history` via `scripts/publish-transcript.sh`. SESSION-PROTOCOL.md at the §5-amended version. | + +**OHM live state**: `ohm.wiggleverse.org` serves rfc-app v0.17.0, +deploys.id=21 (last deploy from Session L). v0.13.0 cookie banner + +v0.14.0 /docs route + v0.15.0 Amplitude analytics & session replay + +v0.16.0 per-RFC owner invites + v0.17.0 admin-create user invites +all live in production for the first time. #21 Part C identity- +lifecycle wiring (identify-with-properties on sign-in, +setUserProperties on mid-session changes, amplitude.reset on +sign-out, identify-BEFORE-track on invite-claim paths) shipped +inline with the v0.15.0 / v0.16.0 / v0.17.0 release commits. + +**Roadmap state**: #2–#16 + #21 Part C all shipped (struck through +in `~/projects/wiggleverse/ohm-rfc/ROADMAP.md`). Wave 6 candidates +open: +- #20 Email deliverability (HIGH priority — invites are now the + recipient's first contact with OHM, spam-foldering is silent + invite failure) +- #18 Secure SMTP relay + Gitea webhook (bundles with #20 — shared + DNS edits) +- #19 CONTRIBUTING guides + transcript-linked onboarding +- #21 Part B — SPEC analytics chapter + CONTRIBUTING checklist + (rides #19) +- #17 Repo naming + location alignment (operator-led; subagent + inventories + proposes) +- #1 VM rename (operator-led) +- #22 Pro-analytics-consent copy (BLOCKED — waiting on operator- + drafted + counsel-reviewed copy) +- #21 Part A — deep audit of v0.15.0 taxonomy + autocapture + + replay quality (BLOCKED — needs ~1 week of Amplitude data; v0.17.0 + is <12 hours old) + +**Housekeeping noticed at open**: +- A stale `SESSION-L-TRANSCRIPT-2026-05-28T04-22--INPROGRESS.md` + (4.3 KB) sits next to Session L's final transcript at + `~/git/ohm-infra/`. The final transcript is the load-bearing one + (was published). The INPROGRESS placeholder is leftover. + Recommend the operator delete it (or driver deletes at the end + of M); not load-bearing for Session M's work. +- Three rfc-app worktrees from Session L still on disk under + `~/git/`: + - `rfc-app-v0.15.0-amplitude/` + - `rfc-app-v0.16.0-owner-invite/` + - `rfc-app-v0.17.0-admin-create-user/` + Each release has shipped and merged to `main`; the worktrees are + no longer load-bearing. Could be cleaned up via + `git worktree remove` when the operator next runs through housekeeping. + +--- + +## Dispatch plan + +**Wave 6** runs as three forked subagents, each in its own subsession, +each writing its own transcript per §5 of the protocol. All three +are Track Ω (docs + ops inventory / proposal) — they touch +non-overlapping files in non-overlapping repos, so they run safely +in parallel without merge-conflict risk. None ship code that +runs on OHM in this wave; each produces a feature branch with a +draft + a recommendation block for the operator to review. + +| Sub | Brief | Repo(s) touched | Output | +| --- | --- | --- | --- | +| **M.1** | #19 CONTRIBUTING guides bundled with #21 Part B (SPEC.md analytics chapter + CONTRIBUTING analytics checklist) | `rfc-app` (CONTRIBUTING.md + SPEC.md) + `ohm-rfc` (CONTRIBUTING.md) | Feature branches in both repos with drafts; ready for operator PR review | +| **M.2** | #17 inventory of every repo under `git.wiggleverse.org` + `git.benstull.org` with current name, canonical location, mirror status, and proposed name. Drafts a move-proposal doc. No renames executed. | `ohm-infra` (write proposal here) + read-only across all Wiggleverse / benstull repos | A `REPO-NAMING-PROPOSAL.md` (or similar) for operator to approve | +| **M.3** | #20 deliverability + #18 SMTP/webhook hardening combined inventory. Reads current DNS posture, rfc-app `backend/app/email_*.py`, ohm-infra SMTP/webhook config, Gitea webhook auth surfaces. Drafts a hardened-shape runbook + a rfc-app template-hygiene minor proposal. | `ohm-infra` (runbook) + read-only on `rfc-app` (drafts the proposal as a doc, NOT as code changes) | A hardening runbook + an rfc-app template-hygiene proposal doc | + +**Why no rfc-app code release this wave**: every Wave 6 candidate +either (a) is docs / inventory / proposal that lands as a PR the +operator merges, no version bump, or (b) requires operator-led +gestures (DNS edits, counsel review, rename execution) that can't +happen autonomously. Wave 6 is the "set up Wave 7" wave — +deliverables here become the operator's plate, and Wave 7 picks up +the rfc-app minors (template hygiene, pro-consent copy) and the +ops moves (DNS, renames) that fall out. + +**Dispatch contract for each subagent** (per §5.2): +1. Pre-assigned subsession ID named in the dispatch prompt. +2. Subagent MUST write `~/git/ohm-infra/SESSION-M.-TRANSCRIPT---.md` + before returning a report. +3. Subagent does NOT push code that ships on OHM. Branches only; + operator merges. +4. Subagent does NOT execute operator-led gestures (no DNS edits, + no repo renames, no SMTP provider config changes). +5. Subagent reports back with: branch + commit SHAs, the draft's + one-paragraph PR description, any §19.2 candidates surfaced. + +--- + +## Turn-by-turn arc + +### Arc 1 — Opening + Wave 6 plan + parallel dispatch + +Operator handed off the wheel at 05:20 PST with a thorough brief +explicitly listing Wave 6 candidates and the new §5 subsession- +transcript dispatch shape. Read SESSION-PROTOCOL.md and ROADMAP.md +end-to-end. Identified six Wave-6-eligible items; three blocked +or operator-only (#22 / #21 Part A / #1), three dispatchable now +(#19 + #21B / #17 / #20+#18). Bundling rationale: #21 Part B +explicitly rides #19's CONTRIBUTING PRs per the roadmap; #18 and +#20 explicitly ship together because the same DNS edits serve both. + +Pre-state snapshot above. Then opened this transcript at +`SESSION-M-TRANSCRIPT-2026-05-28T05-26--INPROGRESS.md` and +dispatched the three subagents in parallel at 2026-05-28T05-30 PST. + +Each subagent's dispatch prompt named: +- the subsession ID (M.1 / M.2 / M.3) +- the transcript filename to write at session end +- the binding constraints (never-secret-bytes; no-operator-led- + gestures; no force-push / `--amend` / `git config`) +- the read-only inventory boundary (no DNS edits, no repo + renames, no flotilla deploy, no SMTP provider changes) +- the deliverable shape and the report-back format + +Dispatch summary: + +| Sub | agent state | Scope | +| --- | --- | --- | +| **M.1** | running (background) | `rfc-app/CONTRIBUTING.md` + `rfc-app/SPEC.md` analytics chapter + `ohm-rfc/CONTRIBUTING.md`. Feature branches in both repos. | +| **M.2** | running (background) | `REPO-NAMING-PROPOSAL.md` — inventory + move proposal across `git.wiggleverse.org` + `git.benstull.org`. No renames executed. | +| **M.3** | running (background) | `EMAIL-AND-WEBHOOK-HARDENING-RUNBOOK.md` + `RFC-APP-EMAIL-HYGIENE-PROPOSAL.md`. Read-only DNS / flotilla-overlay / rfc-app code inventory. No DNS or SMTP changes. | + +Driver waits for each completion notification, folds the report ++ subsession-transcript reference into Arc 2 / Arc 3 / Arc 4 as +they return. Cut-state table written after all three are in. + +### Arc 2 — M.2 returns: #17 repo naming inventory + +M.2 completed at 2026-05-28T05-37 PST (~7 min). Full detail: +`SESSION-M.2-TRANSCRIPT-2026-05-28T05-30--2026-05-28T05-37.md`. + +**Inventory totals:** 9 repos on `git.wiggleverse.org` (4 under +`ben.stull/`, 5 under `wiggleverse/`); 3 on `git.benstull.org` +(under `benstull/`: 1 real sha-synced mirror, 1 stub, 1 empty); +9 local-only working dirs catalogued in the proposal. + +**Surfaced friction not in roadmap #17:** +- Three-meta-repos collision (`wiggleverse/meta` + + `wiggleverse/ohm-meta` + `wiggleverse/rfc-0001-human` — + the monorepo `ohm-rfc` has won in practice; three repos for + one job). +- Push-mirror description drift between `ben.stull/rfc-app` and + `benstull/rfc-app`. +- A `ben` username alias on git.wiggleverse.org creating a + 1-hop redirect chain alongside the canonical `ben.stull`. +- `~/git/ohm-infra/` is a plain directory, not a git repo — + matches what the driver caught at session open. M.2 surfaces + it as a §19.2 candidate (see below). + +**Top moves proposed (execution order from the proposal doc):** +1. Archive `wiggleverse/meta` (vestigial; zero-risk warmup). +2. Archive `wiggleverse/ohm-meta` + `wiggleverse/rfc-0001-human` + (requires Q1 operator confirm — per-RFC repos vs. monorepo). +3. Transfer `ben.stull/ohm-rfc` → `wiggleverse/ohm-rfc` — medium + risk; affects flotilla SPEC (~10 places), 4 test files + hardcode `ben/ohm-rfc`, and flotilla's SQLite registry stores + it as `pin_source_repo` + `corpus_repo` — **operator must run a + flotilla update post-move or the next deploy chases a 2-hop + Gitea redirect.** +4. Transfer `ben.stull/rfc-app` → `wiggleverse/rfc-app` — largest + blast radius. Flotilla SPEC §3.1 has a *conceptual* paragraph + explaining why rfc-app gets a benstull mirror — that rationale + dissolves under org-namespace and needs rewriting. Good news: + no shipped frontend artifacts reference these URLs, so end- + users see nothing. +5. Archive `benstull/rfc-app-flotilla` stub (empty placeholder + that contradicts SPEC §3.1's "no benstull mirror for flotilla" + rule). + +**§19.2 candidates surfaced by M.2:** +1. Stop relying on Gitea path aliases — resolve canonical paths + at registry-update time so flotilla's SQLite registry doesn't + silently chase Gitea redirects after a transfer. +2. Concrete `flotilla-core` reservation slot (the + `benstull/rfc-app-flotilla` stub repo is an accidental artifact + of this need). +3. Promote `~/git/ohm-infra/` to a real git repo — currently a + plain directory; transcripts publish to the remote + `wiggleverse/ohm-session-history` but the runbooks, proposals, + and protocol doc that live in ohm-infra have no version + control. + +**Open questions for the operator** (the proposal lists six; +the load-bearing ones): +- **Q1**: per-RFC repos vs monorepo — settles whether step 2 above + happens at all, and may reshape the proposal entirely. +- **Q2**: mirror destination after rfc-app moves. +- **Q3**: fate of the `benstull/rfc-app-flotilla` stub (the + proposal recommends archive; operator confirm). +- **Q4**: promote ohm-infra/ to a real repo (yes? then where?). +- **Q5**: remove the `ben` username alias. +- **Q6**: backup ohm-infra/ before execution session begins. + +**Artifacts written by M.2:** +- `~/git/ohm-infra/REPO-NAMING-PROPOSAL.md` (~33 KB). +- `~/git/ohm-infra/SESSION-M.2-TRANSCRIPT-2026-05-28T05-30--2026-05-28T05-37.md`. + +Zero remote git operations. No Gitea admin actions. Clean +inventory + proposal pass per scope. + +### Arc 3 — M.3 returns: #20 + #18 deliverability & webhook hardening + +M.3 completed at 2026-05-28T05-58 PST (~28 min). Full detail: +`SESSION-M.3-TRANSCRIPT-2026-05-28T05-30--2026-05-28T05-58.md`. + +**Load-bearing finding (top of operator's plate)**: `SMTP_HOST` +and `SMTP_USER` are MISSING from the live flotilla overlay. Only +`SMTP_PORT=587`, `SMTP_STARTTLS=1`, `EMAIL_FROM`, `EMAIL_FROM_NAME` +are present. Either the bootstrap was reshaped post-run and these +dropped, OR they live on the VM out-of-band via systemd override. +**If neither holds, every OHM email path is silently failing +right now** (OTC, beta-access notifications, per-RFC owner invites, +admin-create user invites — all four paths added across v0.7.0 → +v0.17.0). Operator disambiguates next session via +`gcloud ssh ohm-app` + `systemctl show ohm-app.service | grep Environment`. + +**Current DNS posture (`wiggleverse.org`, literal values):** +- SPF: `v=spf1 include:_spf.google.com ~all` (soft-fail) +- DMARC: `v=DMARC1; p=none; rua=mailto:ben@wiggleverse.org` + (monitoring-only, no enforcement) +- DKIM: present at `google._domainkey.wiggleverse.org` + (2048-bit Workspace key) +- MX: standard Google Workspace `aspmx.l.google.com` family + +**SMTP provider identified:** Google Workspace SMTP relay +(`smtp-relay.gmail.com`) — inferred from bootstrap + DKIM + MX +(no direct confirmation since `SMTP_HOST` is missing from the +overlay). + +**Top 3 deployment-side hardening gestures (priority order):** +1. **Delete stale `wiggleverse/meta` Gitea hook** — still active, + points at `http://rfc.wiggleverse.org/api/webhooks/gitea` + (host was deprovisioned 2026-05-27, plain HTTP). 5-second + `curl DELETE`. Trivial cleanup. +2. **Disambiguate the SMTP_HOST gap** (see load-bearing finding + above). Highest user impact if email is actually silently + broken. +3. **DMARC Phase A** — extend the existing `p=none` record with + `pct=100; sp=quarantine; adkim=s; aspf=s`, collect `rua` data + for 1 week, flip to `p=quarantine`. Conservative enforcement + ramp. + +**Top 3 framework-side changes (target rfc-app v0.18.0):** +1. **`build_envelope` shared helper** — adds `Date`, `Message-ID`, + `Auto-Submitted: auto-generated`, `List-Unsubscribe` + + `List-Unsubscribe-Post` (per RFC 8058), and + `multipart/alternative` text+html to all four email paths + (`email_otc.py`, `email_invite.py`, `email.py:_deliver`, + `email.py:_send_bundle`). Today: NONE of these headers are + set; bodies are plain-text-only; Gmail's one-click-unsubscribe + enforcement is unsatisfied for bulk-adjacent watcher + notifications. +2. **Make `GITEA_WEBHOOK_SECRET` required, not optional.** + `config.py:75` is `_optional`; `webhooks.py:43` silently + accepts unsigned POSTs when empty. Insecure-by-default for any + deployment that forgets the binding. Add `RFC_APP_INSECURE_WEBHOOKS=1` + dev-bypass for local dev only. +3. **`outbound_emails` audit table + write on every send.** + Today failures log-and-forget; operator has zero visibility + without grepping VM logs. The audit table seam doubles as a + place to wire bounce-handling later. + +**Webhook posture audit:** signature verification is correct +(`hmac.compare_digest`, HMAC-SHA-256 over raw body) **when the +secret is set** — and on OHM it is. Two structural issues: +(a) silent-accept-when-unset default; (b) no replay-window +(currently mitigated by idempotent cache refreshes, so not yet +exploited but worth a §19.2 candidate or follow-up rfc-app minor). + +M.3 also noted: `wiggleverse/rfc-0001-human` has NO Gitea webhook +configured (the only published RFC's content changes only reach +OHM via the reconciler). Operator confirms whether this is +intentional. Bot lacks admin on `ben.stull/ohm-rfc` (the pin +source), so M.3 couldn't self-check that one. + +**§19.2 candidates surfaced by M.3 (5):** +1. rfc-app: `GITEA_WEBHOOK_SECRET` should be required. +2. rfc-app: outbound-email observability seam (audit table). +3. flotilla: `overlay show` doesn't surface expected-but-missing + keys; needs `overlay validate` verb. +4. flotilla: lacks a `webhook list/check` verb (generic, would + land in `flotilla-core` after extraction per §19.2). +5. rfc-app: From-identity spec to bind `EMAIL_FROM` consumers + + DMARC + Workspace allowed-senders to one source. + +**Artifacts written by M.3:** +- `~/git/ohm-infra/EMAIL-AND-WEBHOOK-HARDENING-RUNBOOK.md` (~21 KB). +- `~/git/ohm-infra/RFC-APP-EMAIL-HYGIENE-PROPOSAL.md` (~9 KB). +- `~/git/ohm-infra/SESSION-M.3-TRANSCRIPT-2026-05-28T05-30--2026-05-28T05-58.md`. + +Read-only DNS / flotilla-overlay / rfc-app code inventory. Zero +DNS edits, zero overlay set, zero code commits. Clean per scope. + +### Arc 4 — M.1 returns (with a stall): #19 + #21B docs landed + +M.1 hit the stream watchdog at 600s-no-progress and the agent was +killed before it could return its report. Its own transcript says +end-time 05:40 PST — meaning it wrote the transcript ~10 minutes +into its run, then spent another ~50 minutes in a "sanity check +before reporting back" phase that produced no streamable output and +got watchdog-killed. + +Per protocol §5.5 the driver MAY write a stub if a subagent fails to +produce a transcript. M.1 did produce a transcript (full 460-line +detailed shape, all the synthesis the driver needs); the stall was +on the report-back-to-driver step. Driver chose **no re-dispatch**: +all deliverables landed cleanly on disk, both feature branches +pushed to canonical (rfc-app to origin + benstull, ohm-rfc to +origin), tree clean. Recovery was: read M.1's transcript directly ++ switch the two repos back to `main` (M.1 left them on the feature +branches) + fold synthesis into this transcript. + +**Pre-state addendum surfaced by M.1's own pre-state**: ohm-rfc +`main` was at `8cce148` when M.1 opened at 05:30, but this +driver's pre-state at 05:26 (above) recorded ohm-rfc `main` at +`47cb2f9`. The operator pushed `8cce148 ROADMAP: +#23 (rename +transcripts SESSION- → SESSION-NNNN.M)` to ohm-rfc/main in +the 4-minute gap between session-open and M.1 dispatch. Recorded +honestly here per the protocol's no-curation rule — this is the +Session-L "don't batch roadmap captures to end-of-session" lesson +applied in real time. **Roadmap item #23 now exists** (transcript- +filename rename) and is captured on ohm-rfc/main outside of any +Session-M subagent's scope. + +**M.1 deliverables (all on feature branches, none merged):** + +| | | +| --- | --- | +| `rfc-app` `feature/contributing-and-spec-analytics` | `213f686 docs: CONTRIBUTING.md + SPEC.md §21 analytics chapter`. Pushed to `origin` + `benstull`. | +| `ohm-rfc` `feature/contributing-roadmap-guide` | `f26fae4 CONTRIBUTING: add roadmap-change guide + operator-only boundary`. Pushed to `origin`. | + +`rfc-app/CONTRIBUTING.md` is new, ~370 lines, covering: PR shape, +strict-descending CHANGELOG convention, RFC 2119 `Upgrade steps:` +discipline, §19.2 candidate practice, test-coverage expectations +characterized from the real ~250-test backend suite, the operator- +only-gestures boundary (with the hard no-secret-bytes rule cited +from Session K Arc 6), and the #21 Part B analytics-instrumentation +checklist as a dedicated section. Worked-example links to Sessions +E (clean small release), I (deploy-fault recovery), K (multi-feature +wave + paused-on-secret), L (squash-merge across three parallel +features + #21 Part C inline). Links to the repo root not specific +transcript filenames — anticipating the #23 rename. + +`rfc-app/SPEC.md` gets new §21 "Analytics instrumentation and +identity", ~440 lines, structured §21.1 event-taxonomy → §21.2 +required prop families → §21.3 autocapture-friendly DOM patterns +→ §21.4 session-replay masking → §21.5 consent-gate contract → +§21.6 identity lifecycle (#21 Part C) → §21.6.1 set vs setOnce +taxonomy → §21.7 cohort-shape implications → §21.8 secret-vs-public +framing → §21.9 §19.2 candidates → §21.10 open question. RFC 2119 +keywords used precisely throughout. The starting v0.17.0 taxonomy +(13 events) laid out verbatim. + +**Placement decision the driver should weigh in on:** M.1 settled +inline on placing the new chapter as §21 (after §20 versioning) +rather than §16 (the naively-correct insertion-after-§15 +notifications). Rationale: §19.2 is a load-bearing project noun +(in CLAUDE.md, SESSION-PROTOCOL.md §3, 12 published transcripts, +historical commit messages) — renumbering would cascade across all +of those. §15 now carries a forward-pointer naming §21 as the peer +cross-cutting analytics chapter. The driver agrees with M.1's call: +the right §16 numbering belongs to a future cross-repo cleanup +session, NOT a docs-PR. Flagged as §19.2 candidate #1 from M.1 +(see below). + +`ohm-rfc/CONTRIBUTING.md` extends the existing 130-line RFC-content +guide (preserved verbatim) with a new "Proposing a change to the +OHM roadmap" section (~80 added lines). Covers: roadmap-change +kinds, ohm-session-history pointer + worked-example links into +Sessions J/K/L roadmap reshuffles, PR-submission gesture with the +**anonymous-PR open question SETTLED inline** (see Arc 5 below), +the operator-only-gestures boundary phrased with the "I'm the only +one that gets to yolo" quote, the two-layer rule. + +### Arc 5 — Anonymous-PR question settlement (M.1 inline) + +M.1 probed `https://git.wiggleverse.org/user/sign_up` and got HTTP +200 with body containing `

Registration is disabled. Please +contact your site administrator.

`. Definitive answer. + +The ohm-rfc CONTRIBUTING text settles on two contributor paths: +Issue-first (default; the operator or next driver session turns it +into a roadmap entry or opens discussion in the issue thread), or +request-account-from-operator (for repeat contributors). Phrased so +the operator can edit down to one path if registration policy +flips later. + +### Arc 6 — Recovery + finalize + +Driver switched rfc-app + ohm-rfc back to `main` (M.1 left them on +feature branches; `git checkout main` on each — clean, "up to date +with origin/main"). Marked M.1 task completed with a description +recording the stall + recovery decision. Wrote this turn-by-turn, +cut state, §19.2 ledger, operator plate, next-session prompt. +Renamed transcript INPROGRESS → final timestamp. About to publish +all four transcripts (M + M.1 + M.2 + M.3) via +`~/git/ohm-infra/scripts/publish-transcript.sh` per §5.6. + +--- + +## Cut state (end of session, 2026-05-28T06-50 PST) + +| | | +| --- | --- | +| `rfc-app` | `main` unchanged at `1456c8b` (Release 0.17.0). New branch `feature/contributing-and-spec-analytics` at `213f686` pushed to `origin` + `benstull` — awaiting operator PR review. Local tree on `main`, clean. | +| `ohm-rfc` | `main` advanced from `47cb2f9` → `8cce148` mid-session (operator pushed #23). New branch `feature/contributing-roadmap-guide` at `f26fae4` pushed to `origin` — awaiting operator PR review. `.rfc-app-version` unchanged at `0.17.0`. Local tree on `main`, clean. | +| `ohm-rfc-app-flotilla` | unchanged at `65c0e55`. v1.0.1. | +| OHM live | unchanged from Session L cut — `deploys.id=21`, v0.17.0. No deploy gesture this session. | +| `ohm-infra` (local dir) | 4 new files this session: this transcript + M.1/M.2/M.3 subsessions. Plus 2 inventory/runbook docs from M.2 (`REPO-NAMING-PROPOSAL.md`) and 2 from M.3 (`EMAIL-AND-WEBHOOK-HARDENING-RUNBOOK.md`, `RFC-APP-EMAIL-HYGIENE-PROPOSAL.md`). | + +| Wave 6 ledger | Status | +| --- | --- | +| #19 CONTRIBUTING guides (rfc-app + ohm-rfc) | 🟡 drafts pushed on feature branches; awaiting operator PR review + merge | +| #21 Part B SPEC.md §21 chapter + CONTRIBUTING checklist | 🟡 drafted on rfc-app feature branch (rides #19's commit); awaiting operator review | +| #17 Repo naming + location alignment (inventory + proposal) | 🟡 proposal doc written at `~/git/ohm-infra/REPO-NAMING-PROPOSAL.md`; six open questions for operator; execution session deferred | +| #20 Email deliverability inventory | 🟡 runbook written; one load-bearing finding (SMTP_HOST possibly missing from overlay); awaiting operator disambiguation + DNS edits | +| #18 SMTP relay + Gitea webhook hardening inventory | 🟡 runbook same as #20 (bundled); webhook code-side proposal in `RFC-APP-EMAIL-HYGIENE-PROPOSAL.md`; awaiting future rfc-app v0.18.0 minor + ops gestures | +| #21 Part A audit | ⏸ blocked — needs ~1 week of Amplitude data; v0.17.0 is <12h old | +| #22 Pro-analytics-consent copy | ⏸ blocked — waiting on operator-drafted + counsel-reviewed copy | +| #1 VM rename | ⏸ operator-led ops gesture | +| #23 Transcript filename rename (`SESSION-` → `SESSION-NNNN.M`) | ✅ added to ROADMAP (ohm-rfc/main `8cce148`, operator pushed mid-session) | + +No rfc-app release shipped this wave; that's Wave 6's intended shape +(set-up wave — deliverables become Wave 7's plate). The plate is +heavy and clearly-named. + +--- + +## §19.2 candidates surfaced this session + +From **M.1**: +1. **SPEC.md §16..§20 renumbering vs. §21-at-end placement.** The + chapter went in as §21 not §16 to preserve the §19.2 / §19.3 + cross-reference graph; a future cleanup session can do the + numerically-clean §16 insertion + cascade across CLAUDE.md + files + SESSION-PROTOCOL.md + 12 transcripts + auto-memory. +2. **Centralized email-hash helper** (SHA-256 of normalized lower- + cased email for #12-style per-RFC `target_email` hashing). +3. **Property-shape CI lint** for `track(` / `identify(` callsites + (allowlist of prop keys, denylist of PII names). +4. **Frontend test runner** (no `vitest`/`jest`/`playwright` today; + the CONTRIBUTING file documents the gap honestly). + +From **M.2**: +5. **Stop relying on Gitea path aliases** — resolve canonical paths + at registry-update time so flotilla's SQLite registry doesn't + silently chase Gitea redirects after a transfer. +6. **Concrete `flotilla-core` reservation slot** — the + `benstull/rfc-app-flotilla` stub is an accidental artifact of + this latent need. +7. **Promote `~/git/ohm-infra/` to a real git repo** — currently a + plain directory; transcripts publish to the remote but runbooks, + proposals, and the protocol doc have no local version control. + +From **M.3**: +8. rfc-app: `GITEA_WEBHOOK_SECRET` should be **required**, not + `_optional`. Insecure-by-default for any deployment that forgets + the binding. +9. rfc-app: **outbound-email observability seam** (`outbound_emails` + audit table + write on every send). Today failures log-and- + forget; doubles as bounce-handling hook later. +10. flotilla: `overlay show` doesn't surface expected-but-missing + keys; needs an `overlay validate` verb. (The session's own + headline finding — possibly-missing SMTP_HOST — would have been + a one-command detection if this existed.) +11. flotilla: lacks a `webhook list/check` verb (generic — would + land in `flotilla-core` after §19.2 extraction). +12. rfc-app: **From-identity spec** to bind `EMAIL_FROM` consumers + + DMARC + Workspace allowed-senders to one source. + +Driver-surfaced (this transcript): +13. **Subagent stream-watchdog stall pattern.** M.1 finished its + work + transcript at minute ~10 but spent ~50 more minutes in a + quiet "sanity check" phase before the watchdog killed it. The + session-protocol §5.3 instruction says transcript-write happens + BEFORE report-back — that's load-bearing, since the report-back + is where the agent dies. Worth a protocol clarification: + "transcript first; report-back is the only thing after; do NOT + add a 'final sanity check' phase between — sanity-check + findings go into the transcript." Or alternatively, a + documented expectation that the driver always re-verifies + deliverables on disk and doesn't depend on the report-back text + alone. + +--- + +## What lands on the operator's plate + +In rough priority order (driver's read): + +### URGENT — investigate before any further Wave 7 work + +1. **Disambiguate the SMTP_HOST / SMTP_USER overlay gap** (M.3 + finding). Run `gcloud ssh ohm-app` + `systemctl show + ohm-app.service | grep Environment` to check whether they're set + via systemd override. If not, OHM has been silently dropping + every email (OTC, beta-access notifications, per-RFC invites, + admin-create invites) — meaning v0.7.0 / v0.9.0 / v0.16.0 / + v0.17.0's user-facing email features are degraded since deploy. + Send a manual test through the admin "Create user + invite" + flow as a probe. + +### HIGH — review + merge the docs PRs + +2. **rfc-app PR** for `feature/contributing-and-spec-analytics` at + `213f686`. Suggested PR description in + `SESSION-M.1-TRANSCRIPT-…md` What-the-driver-needs-to-know #6. + Open on: + - `https://git.wiggleverse.org/ben.stull/rfc-app/pulls/new/feature/contributing-and-spec-analytics` + - `https://git.benstull.org/benstull/rfc-app/pulls/new/feature/contributing-and-spec-analytics` + Operator decides §21-vs-§16 placement — driver concurs with M.1's + §21 call (don't break the §19.2 cross-reference graph). + +3. **ohm-rfc PR** for `feature/contributing-roadmap-guide` at + `f26fae4`. Description also in M.1's transcript. Open on: + - `https://git.wiggleverse.org/ben.stull/ohm-rfc/pulls/new/feature/contributing-roadmap-guide` + +### MEDIUM — schedule + decide + +4. **#17 repo naming proposal** at `~/git/ohm-infra/REPO-NAMING-PROPOSAL.md`. + Six open questions in the proposal that need operator input + before the execution session can be scoped. Q1 (per-RFC repos + vs monorepo) may reshape the proposal entirely; resolve first. + +5. **#20 + #18 deliverability runbook** at + `~/git/ohm-infra/EMAIL-AND-WEBHOOK-HARDENING-RUNBOOK.md`. + Once #1 above is settled, this is the priority-ordered list of + operator gestures. Top three: + (a) `curl DELETE` the stale `wiggleverse/meta` Gitea webhook + pointing at deprovisioned `http://rfc.wiggleverse.org`; + (b) DMARC Phase A (`pct=100; sp=quarantine; adkim=s; aspf=s`); + (c) confirm `wiggleverse/rfc-0001-human` has no webhook (M.3 + noted absence — intentional or oversight?). + +6. **rfc-app v0.18.0 minor** is now the natural next release — + carries the framework-side hardening from M.3: + `build_envelope` shared helper (Date/Message-ID/Auto-Submitted/ + List-Unsubscribe headers + multipart text+html); required + `GITEA_WEBHOOK_SECRET`; `outbound_emails` audit table. + Proposal doc at `~/git/ohm-infra/RFC-APP-EMAIL-HYGIENE-PROPOSAL.md`. + +### LOW / informational + +7. **#23 transcript filename rename** is on ROADMAP now (operator + pushed `8cce148`). This session is the last to use the + `SESSION-` convention before #23 ships; future sessions + will name as `SESSION-NNNN.M` per the new spec. + +8. **Stale Session-L INPROGRESS file** at + `~/git/ohm-infra/SESSION-L-TRANSCRIPT-2026-05-28T04-22--INPROGRESS.md` + (4.3 KB). Final L transcript is published; this is a placeholder + that can be deleted. Driver did NOT delete (operator's call). + +9. **Three rfc-app worktrees from Session L** still on disk: + `~/git/rfc-app-v0.15.0-amplitude/`, `~/git/rfc-app-v0.16.0-owner-invite/`, + `~/git/rfc-app-v0.17.0-admin-create-user/`. All shipped + merged. + Cleanup via `git worktree remove` at the operator's convenience. + +10. **§19.2 candidate #13** (subagent stream-watchdog stall pattern) + is a candidate protocol clarification for SESSION-PROTOCOL.md §5 + — see the candidates list above. + +--- + +## Prompt the operator can paste into the next Claude Code session + +``` +You are the OHM roadmap driver. The previous session (Session M) +ran Wave 6 as three Track Ω inventory/draft subsessions in parallel +(M.1 / M.2 / M.3) under the new §5 subsession-transcript convention. + +**What Session M produced:** +- Two docs feature branches awaiting your PR review: + - rfc-app `feature/contributing-and-spec-analytics` at `213f686` + (CONTRIBUTING.md + SPEC.md §21 analytics chapter, lands #19 + + #21 Part B; placed §21 not §16 to preserve the §19.2 cross- + reference graph — driver concurs with M.1's call) + - ohm-rfc `feature/contributing-roadmap-guide` at `f26fae4` + (CONTRIBUTING extension covering roadmap changes + operator- + only boundary) +- Three new docs on disk at `~/git/ohm-infra/`: + - `REPO-NAMING-PROPOSAL.md` (#17 inventory + proposal; six open + questions need your input) + - `EMAIL-AND-WEBHOOK-HARDENING-RUNBOOK.md` (#18 + #20 deployment- + side gestures, prioritized) + - `RFC-APP-EMAIL-HYGIENE-PROPOSAL.md` (#18 + #20 framework-side + minor proposal — target rfc-app v0.18.0) + +**URGENT to investigate FIRST**: M.3 found `SMTP_HOST` + `SMTP_USER` +missing from the live flotilla overlay. If they aren't being set +via systemd override on the VM, every OHM email path (OTC, +beta-access notifications, per-RFC invites, admin-create invites +— v0.7.0/v0.9.0/v0.16.0/v0.17.0) has been silently dropping mail +since the relevant deploy. First gesture: `gcloud ssh ohm-app` + +`systemctl show ohm-app.service | grep Environment` to check. If +broken, send a probe through the "admin create user + invite" +flow. + +**Wave 7 candidates (priority order):** +1. **Investigate + fix SMTP gap** (above) — possibly an ops-only + gesture if systemd override is sufficient; possibly an rfc-app + minor if the email-send code needs a defensive change. +2. **Operator reviews + merges the two docs PRs from Session M** + (no version bump for either; lands as no-bump docs commits). +3. **rfc-app v0.18.0** — framework-side hardening from + `RFC-APP-EMAIL-HYGIENE-PROPOSAL.md`: `build_envelope` shared + helper (Date/Message-ID/Auto-Submitted/List-Unsubscribe headers + + multipart text+html for all four email paths); make + `GITEA_WEBHOOK_SECRET` required (no longer `_optional`); + `outbound_emails` audit table. Add `RFC_APP_INSECURE_WEBHOOKS=1` + dev-bypass for local dev only. +4. **#17 execution session** — needs Q1 (per-RFC repos vs monorepo) + answered first; then operator runs the moves per + `REPO-NAMING-PROPOSAL.md`. +5. **#20 deployment-side gestures** — DMARC Phase A (`pct=100; + sp=quarantine; adkim=s; aspf=s`), then ramp to `p=quarantine` + after a week of `rua` data; delete stale `wiggleverse/meta` + Gitea hook pointing at deprovisioned rfc.wiggleverse.org. +6. **#22** — operator drafts + counsel-reviews the pro-analytics- + consent copy; subagent wires it in once approved. +7. **#21 Part A** — earliest reasonable target ~2026-06-04 (one + week of Amplitude data post-v0.17.0). Defer until then. +8. **#1 VM rename** — operator-led ops gesture, schedule a + maintenance window. +9. **#23 transcript filename rename** — on ROADMAP at `8cce148`; + pick a session to execute the rename across all 13 published + transcripts + the publish script's filename validator. + +**Hard rules carried forward from Sessions K + L + M:** +- Never EVER ask the operator to paste secret bytes into the + conversation. Always give the operator-run gesture (`pbpaste | + flotilla secret set `). Public bundle-embedded + values (VITE_AMPLITUDE_API_KEY, VITE_TURNSTILE_SITE_KEY) are fine + in-conversation via `flotilla overlay set`. +- Subagent stream-watchdog stalls (Session M new): subagents + MUST write their transcript BEFORE returning a report (already + in §5.3) — and SHOULD NOT add a "final sanity check" phase + between transcript-write and report-back (where M.1 died). + Driver should always re-verify deliverables on disk rather + than depend on the report-back text alone. +- §5 subsession-transcript convention is binding from Session M + onward. + +**Cut state at handoff:** +- rfc-app `main` at `1456c8b` (v0.17.0) +- ohm-rfc `main` at `8cce148` (#23 added by operator mid-Session-M) +- ohm-rfc-app-flotilla `main` at `65c0e55` (v1.0.1) +- ohm-rfc `.rfc-app-version` = `0.17.0` +- OHM live: deploys.id=21, v0.17.0 +- Two feature branches awaiting your PR review (above) +- Next session letter: **N** + +Read `~/git/ohm-infra/SESSION-PROTOCOL.md` and +`/Users/benstull/projects/wiggleverse/ohm-rfc/ROADMAP.md` +end-to-end. Then pick Wave 7's shippable scope. +``` + + +--- + +(Cut state, §19.2 candidates, operator plate, and next-session +prompt all written at session-end.)