# Session M.1 — Subsession transcript > Parent: `SESSION-M-TRANSCRIPT-2026-05-28T05-26--.md` > Date: 2026-05-28 > Goal: Draft `rfc-app/CONTRIBUTING.md`, the `rfc-app/SPEC.md` analytics chapter, > and the `ohm-rfc/CONTRIBUTING.md` roadmap-guide extension — landing roadmap > items #19 (CONTRIBUTING guides + transcript-linked onboarding) and #21 Part B > (standing analytics instrumentation discipline). Push feature branches; do > NOT tag, do NOT bump VERSION, do NOT move the pin, do NOT deploy. > > Outcome: **All three deliverables shipped on feature branches; both branches > pushed to canonical remotes; one open question settled inline.** > > - `rfc-app feature/contributing-and-spec-analytics` at `213f686` — > pushed to `origin` (git.wiggleverse.org) + `benstull` > (git.benstull.org). One commit: CONTRIBUTING.md (new, ~370 lines) + > SPEC.md §21 (new chapter, ~470 lines including the new §15 forward- > pointer to §21). > - `ohm-rfc feature/contributing-roadmap-guide` at `f26fae4` — pushed > to `origin` (git.wiggleverse.org, the only remote). One commit: > CONTRIBUTING.md extended with a new "Proposing a change to the OHM > roadmap" section (~80 added lines on top of the existing 130-line > RFC-content guide). > - Open question on Gitea anonymous-PR posture: **settled inline** — > `https://git.wiggleverse.org/user/sign_up` returns "Registration is > disabled. Please contact your site administrator." The > ohm-rfc CONTRIBUTING text reflects this with two paths (Issue-first > default + request-account for repeat contributors) and is phrased so > the operator can update if registration policy changes. --- ## Pre-subsession state - **rfc-app**: `main` at `1456c8b` (Release 0.17.0). Tags through v0.17.0. No CONTRIBUTING.md existed. `SPEC.md` 4302 lines, chapter sequence §1..§20. - **ohm-rfc**: `main` at `8cce148`. `.rfc-app-version` = `0.17.0`. `CONTRIBUTING.md` existed (130 lines, RFC-content guide); no roadmap-change section. - **ohm-infra (local)**: most recent transcripts Session L (`SESSION-L-TRANSCRIPT-2026-05-28T04-22--2026-05-28T05-17.md`) + Session M's main transcript in-progress (`SESSION-M-TRANSCRIPT-2026-05-28T05-26--INPROGRESS.md`). - **Branches at start**: both repos on `main`, both clean working trees. --- ## Turn-by-turn arc ### Arc 1 — Reconnaissance Read in order: `ohm-rfc/ROADMAP.md` (#19 at L405-456, #21 at L549-731, particularly Part B at L625-655 and Part C at L657-731); `ohm-rfc/CONTRIBUTING.md` (existing 130-line file — RFC-content oriented, no roadmap-change section); `ohm-infra/SESSION-PROTOCOL.md` (§1 naming, §2 transcript shape, §5 subagent-transcript amendment — binding for this subsession); `ohm-infra/SESSION-K-TRANSCRIPT-…md` (whole file — clean-small- release + multi-feature-wave + paused-on-secret patterns; the Arc-6 hard-rule incident around secret bytes); `ohm-infra/SESSION-L-TRANSCRIPT-…md` (whole file — squash-merge integration with #21 Part C identity-lifecycle wiring folded inline across three releases; the addendum cycles around "transcript is the LAST step" discipline); `rfc-app/SPEC.md` selectively (top, §15 around L2259-2732, §16 + §17 boundaries, §19 around L3183, §20 ending around L4132-4302); the §15 → §16 transition was the candidate insertion-point for the new analytics chapter; `rfc-app/CHANGELOG.md` opening + the v0.17.0 / v0.16.0 / v0.15.0 entries (lines 1-413) for the strict-descending convention and RFC 2119 keyword usage examples; `rfc-app/frontend/src/lib/analytics.js` end-to-end (the v0.15.0 wrapper extended in Session L with `{ user_id, properties }` identify signature, `applyProperties` with `__setOnce__` sentinel, `setUserProperties`, anonymize-clears-cache); `rfc-app/frontend/src/App.jsx` lines 40-90 (the App-level identify call with viewer-derived `set` + `setOnce` property bag); `rfc-app/frontend/src/components/InviteClaim.jsx` lines 40-90 (the v0.17.0 identify-BEFORE-track pattern for the admin-create invite claim). ### Arc 2 — Placement decision for the SPEC chapter (settled inline) The roadmap (and the dispatch brief) said "Place AFTER §15 notifications" and "numbering becomes §16 or whatever's next available." The straightforward read is "insert as §16 and renumber existing §16..§20 to §17..§21." A grep for `§16` through `§20` references in SPEC.md surfaced 50+ cross-references across the file, many of them to `§19.2` and `§19.3` specifically. §19.2 specifically is a load-bearing project noun: it's the defer-decisions queue, named that way in CLAUDE.md, in `SESSION-PROTOCOL.md` §3, in every session transcript ("§19.2 candidates surfaced"), and in commit messages going back to Slice 5. Renumbering §19 → §20 would invalidate every external reference and require coordinated edits across at least: rfc-app CLAUDE.md, ohm-rfc-app-flotilla CLAUDE.md, ohm-infra SESSION-PROTOCOL.md, all 12 published session transcripts, the auto-memory files. None of those are within the scope of this subsession's docs-PR. The "or whatever's next available" hedge in the brief covers this case. **Settled inline:** place the new chapter as §21 (after §20 versioning), with a forward-pointer added at the end of §15's preamble naming §21 as the peer cross-cutting analytics chapter. The new chapter's opening explicitly states the placement reasoning so a future reader knows why §21 isn't between §15 and §16. This is a deliberate deferral of the "right" numbering to a future cleanup session that scopes the cross-repo renumber properly — flagged in the report-back to the driver. ### Arc 3 — `rfc-app/CONTRIBUTING.md` draft Branched `rfc-app` at `feature/contributing-and-spec-analytics` off `origin/main` at `1456c8b`. Drafted CONTRIBUTING.md (~370 lines) covering, in order: - Project framing — what rfc-app is, what a deployment is, the difference between framework-PR and content-PR. - "How the project actually evolves" with explicit wiggleverse/ohm-session-history pointer + worked-example links into Sessions E (clean small release), I (recovery from deploy fault), K (multi-feature wave + paused-on-secret), L (squash-merge across three parallel features + #21 Part C wiring inline). The session links are to the repo root rather than specific transcript filenames so they don't break when transcripts get renamed via roadmap #23. - Contribution flow — Issue-first for structural changes, branch naming convention (matched against the driver session's `feature/v-` shape), explicit "do not bump VERSION, do not tag, do not deploy, do not touch any .rfc-app-version pin." - CHANGELOG strict-descending convention with a worked entry template (mirroring v0.15.0/v0.16.0/v0.17.0 actual shapes) and the operator-fills-version-at-merge-time note. - RFC 2119 keyword discipline per SPEC §20.4 — MUST / MUST NOT / SHOULD / SHOULD NOT / MAY — with the cross-version-upgrade composition rule cited. - §19.2 candidate discipline — explicit "don't scope-creep, name the deferral." Cited v0.11.0's three new candidates (Session K) and v0.15.0's two (Session L) as worked examples. - Test-coverage expectations characterized from the actual ~250-test backend suite. Named specific vertical files (`test_admin_create_user_invite_vertical.py`, etc.) and the `PYTHONPATH=. .venv/bin/pytest -q` ergonomic (still §19.2). Noted that there is no frontend test runner today — the discipline is build-clean + backend HTTP contract coverage. - Analytics instrumentation checklist (the #21 Part B CONTRIBUTING artifact bullet): (1) named events, (2) autocapture-friendly DOM, (3) replay masking, (4) PR description discipline. Plus a pointer to SPEC §21.6 identity-lifecycle contract for any new claim/sign-in/invite path. - Operator-only gestures explicitly enumerated, with the hard no-secret-bytes-in-conversation rule named (Session K Arc 6 origin cited). - "When in doubt" closer. The CONTRIBUTING file uses MIT License as the rfc-app `LICENSE` shape; checked `ls rfc-app/LICENSE` showed it exists. ### Arc 4 — `rfc-app/SPEC.md` §21 chapter draft Appended new §21 "Analytics instrumentation and identity" (~440 lines) at the end of SPEC.md (after §20.7), structured as: - §21 preamble — what this chapter codifies, framework-neutral posture, the placement-as-§21 reasoning (the cross-ref-preservation rationale from Arc 2). - §21.1 event-taxonomy conventions — Title Case "Subject Verb", stability + dashboard-name-match, opaque-ids-only-in-props, casing consistency. The starting v0.17.0 taxonomy (13 events) laid out verbatim. - §21.2 required prop families — navigation / auth-state / authored- action / admin-action / invite-side / claim-side. The hashed target_email convention (SHA-256 of normalized lowercased email) named for the #12 per-RFC invite case where the invitee isn't yet a user. - §21.3 autocapture-friendly DOM patterns — stable visible text, aria-label on icon-only buttons, `data-amp-track-*` for repeated rows, `data-amp-track-suppress` for noise surfaces, with a worked HTML example. - §21.4 session-replay masking — credentials MUST mask, PII SHOULD mask, privacy-policy alignment MUST hold, selective redaction preferred over whole-form. The `.amp-mask` / `data-amp-mask` convention named with the qualifier "whichever the wrapper's SDK version uses" (acknowledging that the Amplitude SDK's masking attribute name has varied across versions; the wrapper's bootstrap comment is the live source of truth). - §21.5 consent-gate contract — five MUSTs: pre-consent no init/no network/no recording; denied→granted init at consent moment; granted→denied setOptOut(true) within one tick; no double-init on re-grant; build-time env-var resolution with one-warn no-op when unset. - §21.6 identity lifecycle (per #21 Part C) — sign-in / state-change / sign-out / invite-claim / inviter-side identification, each with MUST language. The User-Signed-Out track-BEFORE-anonymize ordering called out as load-bearing. - §21.6.1 set vs setOnce taxonomy — explicit classification rule with worked examples on each side (role / permission_state / passcode_set / device_trusted = set; first_sign_in_at / account_created_at / invited_by_admin_id / invited_at / claim_method = setOnce). - §21.7 cohort-shape implications (informative) — four worked questions the dashboard can now answer with the conventions: admin-invite vs OTC funnel by week; passcode-set rate in first session by claim path; per-RFC owner-invited-contributor counts; invite-send-to-claim gap by inviter. - §21.8 secret-vs-public framing — overlay binding for the public Amplitude key, secret binding for the Turnstile siteverify secret half. The pbpaste-pipe canonical gesture for OHM-shape deployments named. - §21.9 §19.2 candidates surfaced — four (session-replay-specific consent category; bundle-size budget measurement; property-shape CI lint; centralized email-hash helper). - §21.10 open question — consent-category split timing tied to the next privacy-policy revision. Then added a forward-pointer paragraph at the end of §15's preamble (the "section commits the surface..." paragraph) noting §21 as the peer cross-cutting analytics chapter and explicitly saying the two surfaces don't overlap (notifications = signals-of-others-acting; analytics = observability of use). The chapter uses RFC 2119 keywords precisely throughout. No "should probably" / "might want to" weasel-words inside MUST/ SHOULD steps. Cited frontend/src/lib/analytics.js, App.jsx, InviteClaim.jsx, AcceptInvitation.jsx as worked-example references. ### Arc 5 — `ohm-rfc/CONTRIBUTING.md` extension Branched `ohm-rfc` at `feature/contributing-roadmap-guide` off `origin/main` at `8cce148`. Strategy: extend the existing 130-line RFC-content guide with a new section rather than create a competing file. The existing sections (RFC discussions, RFC PR format, RFC numbering, values, license) are preserved verbatim; the roadmap-change section appends after the License block as a new "Proposing a change to the OHM roadmap" heading + subsections. Section structure: - What kinds of roadmap changes are welcome (5 bullets — add, reshape, claim a version slot, strike-through on ship, §19.2 candidate at the OHM layer). - How OHM actually evolves — wiggleverse/ohm-session-history pointer + worked-example links into Sessions J (mid-build version-slot reshuffle when docs-feature took v0.14.0), K (mid-session capture of #16/#17/#18/#19), L (mid-build capture of #20/#21/#22 + Part C wiring + #23). - How to submit a roadmap PR — including the now-settled Gitea registration-disabled question. - The operator-only gestures — phrased with the operator's "I'm the only one that gets to yolo" quote. Six bullets: flotilla deploy, .rfc-app-version pin bump, secret-set, overlay-set on OHM live, repo rename, Gitea account provisioning, PR merge. Hard no-secret-bytes-in-conversation rule named. - The two-layer rule — framework change → upstream rfc-app PR; OHM-specific change → here. - "When you're not sure" closer. ### Arc 6 — Open question on Gitea registration posture, settled Per the dispatch brief, probed git.wiggleverse.org: - `https://git.wiggleverse.org/api/v1/version` returned `{"version":"1.26.2"}` — confirming the instance is up. - `https://git.wiggleverse.org/user/sign_up` returned HTTP 200 but the body contained `

Registration is disabled. Please contact your site administrator.

` — definitive answer. Registration is **disabled**, meaning outside contributors without a Gitea account cannot directly open PRs. The ohm-rfc CONTRIBUTING text settles on two 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). The text is phrased so the operator can edit it down to one path if they choose to flip registration policy later. The rfc-app CONTRIBUTING file doesn't carry this question explicitly — its operator-gestures section already covers "contributors do not have direct push access," and the Issue-first path is the default mode for PRs the operator hasn't expected. ### Arc 7 — Commits + push `rfc-app`: - Staged CONTRIBUTING.md (new) + SPEC.md (modified). - Committed `213f686` "docs: CONTRIBUTING.md + SPEC.md §21 analytics chapter" with a HEREDOC commit body naming the deliverables and the placement-as-§21 reasoning. Identity defaulted to `Ben Stull ` (matches the operator's prior commits on this repo; no git config edit). Pushed to both remotes: `origin` (git.wiggleverse.org) and `benstull` (git.benstull.org) with `-u` flag; both succeeded. `ohm-rfc`: - Staged CONTRIBUTING.md (modified). - Committed `f26fae4` "CONTRIBUTING: add roadmap-change guide + operator-only boundary" with a HEREDOC commit body. Same operator identity. Pushed to `origin` (the only remote on this repo) with `-u`; succeeded. No tags, no VERSION bump, no `.rfc-app-version` pin move, no `flotilla deploy`. All operator-only gestures untouched. ### Arc 8 — Transcript Wrote this subsession transcript at `~/git/ohm-infra/SESSION-M.1-TRANSCRIPT-2026-05-28T05-30--2026-05-28T05-40.md` per the §5 subagent-transcript convention. Parent reference in the header. The driver publishes this alongside its own Session-M transcript via `~/git/ohm-infra/scripts/publish-transcript.sh`. --- ## Cut state (end of subsession) | | | | --- | --- | | `rfc-app` | `feature/contributing-and-spec-analytics` at `213f686`; pushed to `origin` + `benstull`. `main` unchanged at `1456c8b`. Nothing tagged. | | `ohm-rfc` | `feature/contributing-roadmap-guide` at `f26fae4`; pushed to `origin`. `main` unchanged at `8cce148`. `.rfc-app-version` unchanged at `0.17.0`. | | OHM live | unchanged — `deploys.id=21`, v0.17.0. No deploy gesture performed. | | `ohm-rfc-app-flotilla` | unchanged at `65c0e55`. | | Operator-only gestures | none touched (no tag, no version bump, no pin move, no deploy, no secret-set). | | Subsession M.1 ledger | Status | | --- | --- | | #19 rfc-app CONTRIBUTING.md | drafted on feature branch, pushed (origin + benstull) | | #19 ohm-rfc CONTRIBUTING.md (extension) | drafted on feature branch, pushed (origin) | | #21 Part B SPEC.md §21 analytics chapter | drafted on feature branch, pushed (origin + benstull); rides the rfc-app CONTRIBUTING commit | | #19 anonymous-PR open question | settled — registration disabled on the Gitea instance; documented inline with two contributor paths | --- ## §19.2 candidates surfaced 1. **SPEC.md §16..§20 renumbering vs. §21-at-end placement.** The roadmap brief's "place AFTER §15" naturally implies insert- as-§16-and-shift-§16..§20-down, but renumbering would cascade through 50+ cross-references, including `§19.2` (the project's defer-decisions noun, used in CLAUDE.md, the session protocol, 12 published transcripts, and historical commit messages). This subsession chose the lower-risk placement-as-§21 with a §15 forward-pointer, and documented the choice in the new chapter's preamble. A future cleanup session that wants to do the numerically-clean §16 placement needs to scope: rfc-app SPEC renumber + rfc-app CLAUDE.md update + ohm-rfc-app-flotilla CLAUDE.md cross-ref update + ohm-infra SESSION-PROTOCOL.md reference sweep + all 12 published transcript header notes (in wiggleverse/ohm-session-history) + the auto-memory feedback files. None of those are within this subsession's docs-PR scope; flagged for operator's call. 2. **Centralized email-hash helper.** §21.2 specifies SHA-256 of the normalized lower-cased email as the hashing function for the per-RFC `target_email` fingerprint, but no helper exists in the framework today. A small `frontend/src/lib/hash.js` or `backend/app/hash.py` that centralizes normalization + hashing would make the contract enforceable rather than relying on per-callsite re-implementation. Captured in SPEC §21.9 as well. 3. **Property-shape CI lint.** The conventions in SPEC §21.1 / §21.2 are review-enforced today. A small lint (grep `track(` / `identify(` callsites with a property-key allowlist + a PII-name denylist) would catch drift mechanically. Captured in SPEC §21.9. 4. **Frontend test runner.** rfc-app/frontend has no test runner today (no `vitest`, no `jest`, no `playwright` — only `dev` / `build` / `preview` scripts). The discipline that the CONTRIBUTING file documents (backend vertical tests carry the regression boundary; frontend ships build-clean) is honest about the current state but is itself a §19.2 candidate worth flagging for a future session — a small `vitest` setup + a few smoke tests against the wrapper modules (analytics.js, consent.js, api.js) would catch the kind of structural breakage a frontend-only release would otherwise discover at deploy time. --- ## What the driver needs to know 1. **Both branches are pushed and ready for PR.** The driver/operator can open PRs by visiting: - `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` (the mirror; merge here too if the operator's release flow normally syncs both — Session K/L precedent is that operator pushes tags to both but PR-opens on one and merges the merge- commit to both. The operator's call.) - `https://git.wiggleverse.org/ben.stull/ohm-rfc/pulls/new/feature/contributing-roadmap-guide` 2. **The SPEC chapter is §21, not §16.** This is a deliberate docs-PR-scope-only choice (Arc 2 / §19.2 candidate #1 above). The new chapter's preamble explains the placement to readers. If the operator prefers the §16 insertion with full renumber, that's a separate larger PR that touches CLAUDE.md files and transcript headers across multiple repos — too large for this subsession. 3. **Anonymous-PR question settled inline.** Registration is disabled on git.wiggleverse.org (confirmed via the sign-up page). The ohm-rfc CONTRIBUTING text is phrased to update easily if the operator changes that posture later. 4. **No operator gestures were performed.** No tag, no VERSION bump, no pin move, no deploy, no secret-set, no overlay-set. Both branches are drafts ready for operator review + merge at the operator's chosen cadence. 5. **No secret bytes in either commit body or in this transcript.** The hard rule was honored throughout. The SPEC chapter and both CONTRIBUTING files reference secrets by name only (`SMTP_PASSWORD`, `AMPLITUDE_API_KEY`, `CLOUDFLARE_TURNSTILE_SECRET`). 6. **Suggested PR descriptions** (operator can paste verbatim into the new-PR form): - `rfc-app` PR: "Lands roadmap item #19 (CONTRIBUTING + transcript-linked onboarding) and #21 Part B (standing analytics instrumentation discipline) as a single docs commit. Adds CONTRIBUTING.md (~370 lines) covering PR shape, CHANGELOG strict-descending convention, RFC 2119 upgrade-steps discipline, §19.2 candidate practice, test-coverage expectations from the real ~250-test backend suite, the operator-only-gestures boundary, and the #21 Part B analytics-instrumentation checklist (named events / autocapture-friendly DOM / replay masking / PR-description discipline). Adds SPEC.md §21 'Analytics instrumentation and identity' (~440 lines) codifying event taxonomy, required prop families, autocapture DOM patterns, session-replay masking conventions, the consent-gate contract, the #21 Part C identity lifecycle (identify-with-properties on sign-in; setUserProperties on state change; reset on sign-out; identify-BEFORE-track on invite-claim), the set-vs-setOnce taxonomy with classification rule, cohort-shape implications, and four new §19.2 candidates. Placed as §21 rather than §16 to preserve the load-bearing §19.2 / §19.3 cross-reference graph; reasoning in the chapter preamble. §15 carries a new forward- pointer naming §21 as the peer cross-cutting analytics chapter. No code change; no VERSION bump; rides a future release or lands as a no-bump docs commit at operator discretion." - `ohm-rfc` PR: "Lands roadmap item #19's OHM-side artifact. Extends the existing 130-line RFC-content CONTRIBUTING.md with a new 'Proposing a change to the OHM roadmap' section (~80 lines) covering roadmap-change kinds, the wiggleverse/ohm-session-history pointer with worked-example links into Sessions J/K/L, the PR-submission gesture (with the open question on Gitea registration settled inline: git.wiggleverse.org currently has registration disabled, so contributors choose between Issue-first and request-account paths), the explicit operator-only-gestures boundary with the 'I'm the only one that gets to yolo' quote, and the two-layer rule. No version target." --- ## Files written - `/Users/benstull/git/rfc-app/CONTRIBUTING.md` (new) - `/Users/benstull/git/rfc-app/SPEC.md` (modified — new §21 chapter + §15 forward-pointer paragraph) - `/Users/benstull/projects/wiggleverse/ohm-rfc/CONTRIBUTING.md` (modified — new "Proposing a change to the OHM roadmap" section appended after the License block) - `/Users/benstull/git/ohm-infra/SESSION-M.1-TRANSCRIPT-2026-05-28T05-30--2026-05-28T05-40.md` (this file)