add SESSION-L-TRANSCRIPT-2026-05-28T04-22--2026-05-28T05-17.md
This commit is contained in:
@@ -0,0 +1,837 @@
|
||||
# Session L — Transcript
|
||||
|
||||
> Date: 2026-05-28
|
||||
> Goal: Execute Wave 5 of `ohm-rfc/ROADMAP.md` as the autonomous
|
||||
> driver per the "Operating instructions for the next session"
|
||||
> section. The operator's bedtime prompt asked for three items
|
||||
> dispatched in parallel — **#13 Amplitude → v0.15.0** (Track A;
|
||||
> originally wave-paused on operator-provided `AMPLITUDE_API_KEY`),
|
||||
> **#12 Owner-only invite → v0.16.0** (Track B), and
|
||||
> **#16 Admin-create user + invite email → v0.17.0** (Track C
|
||||
> extension; layers on top of v0.9.0's `/admin/users` surface).
|
||||
> Mid-session, the operator returned to the keyboard and added
|
||||
> three roadmap items + a meaningful enlargement of #13's scope.
|
||||
>
|
||||
> Outcome: **All three Wave-5 features shipped to OHM live.**
|
||||
>
|
||||
> - **#13 Amplitude shipped as rfc-app v0.15.0 to OHM live**
|
||||
> (`deploys.id=19`, all 9 phases green, `/api/health` returns
|
||||
> `{"version":"0.15.0","status":"ok"}`). Vendor's recommended
|
||||
> `@amplitude/unified` package with `initAll(KEY, { analytics:
|
||||
> { autocapture: true }, sessionReplay: { sampleRate: 1 } })`.
|
||||
> Bound via `flotilla overlay set ohm-rfc-app
|
||||
> VITE_AMPLITUDE_API_KEY=…`, not `secret set` — Amplitude
|
||||
> browser keys are bundle-embedded (same nature as
|
||||
> `VITE_TURNSTILE_SITE_KEY`). The "wave pause for operator
|
||||
> secret-set" the bedtime prompt anticipated didn't materialize
|
||||
> in that shape; instead the operator returned mid-session,
|
||||
> provisioned the Amplitude project, surfaced the vendor's
|
||||
> installation prompt (which contained the public key inline),
|
||||
> and the binding settled as overlay. Conversation-layer hard
|
||||
> rule respected: the key value was operator-provided
|
||||
> voluntarily, not asked for.
|
||||
> - **#12 Owner-only invite shipped as rfc-app v0.16.0 to OHM
|
||||
> live** (`deploys.id=20`, all 9 phases green, `/api/health`
|
||||
> returns `{"version":"0.16.0","status":"ok"}`). Per-RFC
|
||||
> `contributor` / `discussant` roles, transactional email,
|
||||
> migration `018_rfc_invitations.sql`.
|
||||
> - **#16 Admin-create user + invite shipped as rfc-app v0.17.0
|
||||
> to OHM live** (`deploys.id=21`, all 9 phases green,
|
||||
> `/api/health` returns `{"version":"0.17.0","status":"ok"}`).
|
||||
> Admin-driven user provisioning with optional custom message,
|
||||
> opaque-token claim flow, migration `019_user_invite_tokens.sql`.
|
||||
>
|
||||
> Plus three new roadmap items captured mid-session
|
||||
> (`ohm-rfc/ROADMAP.md` items #20, #21, #22 — see arc 7 below):
|
||||
> email deliverability (DNS + SMTP + template hygiene), Amplitude
|
||||
> audit + standing instrumentation discipline (with Part C
|
||||
> identity-lifecycle work folded inline into all three v0.15.0 /
|
||||
> v0.16.0 / v0.17.0 release commits), and pro-analytics-consent
|
||||
> copy on the privacy/cookies opt-in (with legal caveats spelled
|
||||
> out so the drafting accounts for 501(c)(3)-planned-not-granted
|
||||
> status, "no external sharing" honesty about Amplitude as the
|
||||
> processor, and consent-coercion risk).
|
||||
>
|
||||
> Side effect of the pin jumping from `0.12.0` → `0.15.0` → `0.16.0`
|
||||
> → `0.17.0`: OHM now also runs v0.13.0's cookie-consent banner
|
||||
> and v0.14.0's `/docs` route, which had been integrated on
|
||||
> `main` since Session J but were not currently deployed because
|
||||
> the pin had rolled back during Session K's Wave-4 sequence.
|
||||
|
||||
---
|
||||
|
||||
## Pre-session state
|
||||
|
||||
- **rfc-app**: `main` at `b3f1b15` (Release 0.12.0 — Turnstile).
|
||||
`VERSION` = `0.12.0`. `frontend/package.json#version` = `0.12.0`.
|
||||
Tags shipped through v0.14.0; the v0.x lineage is
|
||||
`v0.2.0, .1, .2, .3, v0.3.0, v0.4.0, v0.5.0, v0.6.0, v0.7.0,
|
||||
v0.8.0, v0.9.0, v0.10.0, v0.11.0, v0.12.0, v0.13.0, v0.14.0`.
|
||||
Last migration on disk: `017_device_trust.sql` (slot 016
|
||||
reserved + skipped per Session K).
|
||||
- **ohm-rfc**: `.rfc-app-version` = `0.12.0`. The pin was "below"
|
||||
v0.13.0 (cookie banner) and v0.14.0 (`/docs`) — those features
|
||||
were integrated on rfc-app `main` but not currently deployed
|
||||
to OHM. Any Wave-5 pin bump past `0.12.0` would pick up both
|
||||
side-effect features; flagged in arc 1 + reconfirmed at the
|
||||
arc 6 deploy. ROADMAP main was at `9654cbb` (post-Session-K
|
||||
ROADMAP: #10 v0.12.0 shipped row).
|
||||
- **OHM live**: serving `v0.12.0` per
|
||||
`https://ohm.wiggleverse.org/api/health` returning
|
||||
`{"version":"0.12.0","status":"ok"}`. `flotilla deploy log
|
||||
ohm-rfc-app` showed last successful deploy as `deploys.id=18`
|
||||
(v0.12.0, succeeded, 11:18 UTC) — the post-`TURNSTILE_REQUIRED=true`
|
||||
re-deploy from Session K that put abuse defense fail-closed.
|
||||
- **ohm-rfc-app-flotilla**: `main` at `65c0e55` (CLAUDE.md
|
||||
secret-set gesture correction). No flotilla work expected in
|
||||
Wave 5; none performed.
|
||||
- **ohm-infra**: most recent transcript is Session K
|
||||
(`SESSION-K-TRANSCRIPT-2026-05-28T03-20--2026-05-28T03-55.md`).
|
||||
Next letter is L (Session K = previous driver; Session J = the
|
||||
parallel docs-feature / v0.14.0 session that ran during
|
||||
Session I's wind-down).
|
||||
- **Hard rule baked in mid-Session-K**: never EVER ask the operator
|
||||
to paste secret bytes into the conversation. Stayed honored
|
||||
through this session — the Amplitude key the operator pasted is
|
||||
a public bundle-embedded value (same nature as
|
||||
`VITE_TURNSTILE_SITE_KEY`), which CLAUDE.md explicitly carves
|
||||
out as fine to receive in conversation. The driver did not ask
|
||||
for any value; the operator volunteered the vendor's
|
||||
installation prompt (which contained the key inline) +
|
||||
confirmed it. Reconciliation between the bedtime prompt's
|
||||
"wave-pauses on AMPLITUDE_API_KEY" framing and the actual
|
||||
vendor-recommended overlay binding is documented as the
|
||||
v0.15.0 CHANGELOG's "Caveat — overlay binding" section.
|
||||
|
||||
---
|
||||
|
||||
## Turn-by-turn arc
|
||||
|
||||
The session moves through nine arcs, in order.
|
||||
|
||||
### Arc 1 — Reconnaissance + dispatch
|
||||
|
||||
The driver read `~/git/ohm-infra/SESSION-PROTOCOL.md` and
|
||||
`/Users/benstull/projects/wiggleverse/ohm-rfc/ROADMAP.md`
|
||||
end-to-end, verified pre-session ground truth (rfc-app `main` at
|
||||
`b3f1b15`, OHM serves v0.12.0 at `deploys.id=18`, ohm-rfc pin at
|
||||
`0.12.0`), and dispatched three subagents in a single message
|
||||
with parallel `Agent` tool uses, `subagent_type: general-purpose`,
|
||||
running in the background. Each subagent created its own
|
||||
`git worktree add` in `/Users/benstull/git/rfc-app-<target-version-
|
||||
slug>` (the `Agent` tool's worktree isolation only covers the
|
||||
flotilla CWD, not the rfc-app CWD subagents needed — Session K
|
||||
lesson). Each prompt was self-contained: full roadmap text,
|
||||
target version, reserved migration slot (018 for ν, 019 for ο,
|
||||
none for ξ), descending-order CHANGELOG insertion point, hard
|
||||
constraints (do NOT tag, do NOT touch the ohm-rfc pin, do NOT
|
||||
run `flotilla deploy`, no force-push, no `--amend`, no secret
|
||||
bytes, no `git config` edits), and a structured reporting brief.
|
||||
|
||||
Worktree base discovered via `ls -la /Users/benstull/git/`. One
|
||||
ground-truth correction caught early: `~/git/ohm-infra` is NOT a
|
||||
git repo locally — it's a working directory the publish script
|
||||
clones the remote `wiggleverse/ohm-session-history` into. Session
|
||||
K's prompt phrased it correctly; the driver's first `cd
|
||||
~/git/ohm-infra && git log` failed and surfaced the right model.
|
||||
|
||||
### Arc 2 — Operator returns mid-session: Amplitude scope settled
|
||||
|
||||
While the three subagents were running in the background, the
|
||||
operator returned to the keyboard and surfaced the Amplitude
|
||||
provisioning:
|
||||
|
||||
1. "I provisioned an Amplitude instance."
|
||||
2. The vendor's installation-wizard prompt (verbatim — it included
|
||||
the API key inline as a literal string argument:
|
||||
`amplitude.initAll('741709e30574cc8314b83b763f675c32', {
|
||||
"analytics": { "autocapture": true }, "sessionReplay":
|
||||
{ "sampleRate": 1 } });` — and recommended the
|
||||
`@amplitude/unified` package).
|
||||
3. "I have the amplitude API key… can set that secret when we're
|
||||
ready. Or, if it's not a secret and is really a public thing
|
||||
that's in JavaScript, can provide it here."
|
||||
|
||||
The driver settled three things in one response:
|
||||
|
||||
- **Secret vs. public**: the Amplitude browser key is bundle-
|
||||
embedded by design (visible in dev tools; vendor's own prompt
|
||||
shows it as a literal string argument). Same nature as
|
||||
`VITE_TURNSTILE_SITE_KEY` from v0.12.0, which is explicitly
|
||||
public per CLAUDE.md. The right binding is `flotilla overlay
|
||||
set`, not `flotilla secret set`. The roadmap row #13's "new
|
||||
secret: AMPLITUDE_API_KEY" wording predated vendor
|
||||
consultation; corrected at the v0.15.0 CHANGELOG + the
|
||||
shipped-row update.
|
||||
- **Scope change**: the vendor recommends `@amplitude/unified`
|
||||
(analytics + session replay bundled) instead of the
|
||||
`@amplitude/analytics-browser` the dispatch brief specified.
|
||||
`sampleRate: 1` (100% session replay) is the vendor default
|
||||
— meaningfully larger privacy footprint than events-only. The
|
||||
driver surfaced three options to the operator via
|
||||
AskUserQuestion: events only / events + replay 10% / events
|
||||
+ replay 100% (vendor default). The operator picked **events
|
||||
+ session replay @ 100% (vendor default)**.
|
||||
- **In-flight subagent**: ξ was already running under the
|
||||
pre-vendor brief; no `SendMessage` tool available in this
|
||||
harness to redirect it; chosen path is "let ξ finish, driver
|
||||
post-corrects on the branch" since the post-correction is
|
||||
tight and mechanical (package swap, init-call rewrite,
|
||||
CHANGELOG MUST-step rewrite). The wrapper structure, event
|
||||
taxonomy, and consent integration ξ would produce were
|
||||
brief-correct and would survive the post-correction
|
||||
unchanged.
|
||||
|
||||
### Arc 3 — Operator surfaces roadmap items #20, #21, #22
|
||||
|
||||
Across three messages while the subagents were running, the
|
||||
operator added three new roadmap items:
|
||||
|
||||
- **#20 Email deliverability** — "do whatever we can to prevent
|
||||
invites from going to the spam folder." Captured during arc 5
|
||||
before any subagent had returned. Scope spelled out: DNS (SPF,
|
||||
DKIM, DMARC, BIMI follow-up), SMTP-provider posture (verified
|
||||
sender domain, warm-up), From / Reply-To hygiene (no
|
||||
`noreply@`), rfc-app template hygiene (plain-text alternative,
|
||||
`List-Unsubscribe` + `List-Unsubscribe-Post` per RFC 8058,
|
||||
bounce handling), engagement signals, monitoring (mail-tester,
|
||||
Postmaster Tools, DMARC `rua=`). Overlap with #18 (Secure SMTP
|
||||
+ Gitea webhook) flagged explicitly; likely shipped together.
|
||||
Two-layer rule honored: rfc-app template hygiene is
|
||||
framework-wide, DNS + SMTP-provider config + BIMI logo are
|
||||
OHM-specific. Sequencing: don't block #12 / #16 on this; the
|
||||
real-world inbox/spam data from those first invite blasts
|
||||
informs the #20 sweep afterward. Committed to ohm-rfc at
|
||||
`2072663`.
|
||||
- **#21 Amplitude audit + standing instrumentation discipline** —
|
||||
captured after the operator extended Amplitude scope in arc 2.
|
||||
Two-part structure: Part A (deep audit of v0.15.0's taxonomy +
|
||||
autocapture + replay quality once a week of real data is in)
|
||||
+ Part B (standing discipline: SPEC.md analytics chapter,
|
||||
rfc-app CONTRIBUTING checklist, optional CI lint).
|
||||
Subsequently extended with **Part C** after the operator said:
|
||||
"ensure a new user in Amplitude is created when an invite is
|
||||
sent and we have a user with the OHM user ID set on that
|
||||
event"; "appropriate pattern for setting user properties on
|
||||
login + clearing on logout"; "implement Amplitude best
|
||||
practices from the very get-go". Part C captures the
|
||||
user-identity lifecycle contract (setUserId + identify
|
||||
properties on sign-in; setUserProperties on mid-session state
|
||||
change; reset on sign-out; identify-BEFORE-track on invite-
|
||||
claim so the Amplitude record is created with the OHM user_id
|
||||
from the first event rather than as an anonymous device that
|
||||
retroactively links). Part C explicitly names what "ships
|
||||
inline in Session L" vs. what waits for the data-informed
|
||||
audit. Committed to ohm-rfc at `f006d33` (Parts A + B), then
|
||||
`1c88f71` (Part C added).
|
||||
- **#22 Pro-analytics-consent copy on the privacy/cookies
|
||||
opt-in** — captured later: "add language to the privacy /
|
||||
cookies opt-in that asks people to consider allowing analytics
|
||||
and we promise we won't share it externally — we're a
|
||||
non-profit and will only use it to make the experience better
|
||||
(if that language is legal)." Legal caveats spelled out in
|
||||
the item itself: 501(c)(3) is **planned not granted** per the
|
||||
project memory (copy MUST NOT claim "we are a 501(c)(3)"
|
||||
today; defensible alternatives are "being formed as", "non-
|
||||
commercial project", or neutral "operated by Wiggleverse Org"
|
||||
with an explainer); "never shared externally" is literally
|
||||
untrue because Amplitude IS an external service (defensible
|
||||
framing is "don't sell or trade" + "Amplitude holds it on our
|
||||
behalf"); purpose-limitation pledges are enforceable; GDPR /
|
||||
CCPA caution about coerced consent; withdrawal must stay easy.
|
||||
Drafting flow: operator drafts → counsel reviews → subagent
|
||||
wires approved text into banner + `/privacy` + `/cookies`
|
||||
(the wiring step is small and mechanical). Committed to
|
||||
ohm-rfc at `26b4684`.
|
||||
|
||||
### Arc 4 — Session ξ returns; driver post-corrects on its branch
|
||||
|
||||
ξ completed at `0fd8c52` on `feature/v0.15.0-amplitude` (origin +
|
||||
benstull). 14 files touched; the nine-event taxonomy (Page
|
||||
Viewed, RFC Viewed, User Signed In / Signed Out, RFC Proposed,
|
||||
PR Opened, Comment Posted, Beta Access Requested, Admin
|
||||
Permission Decision) wired into App.jsx, Login.jsx, ProposeModal,
|
||||
PRModal, RFCView, RFCDiscussionPanel, PRView, Admin. Consent gate
|
||||
read at `frontend/src/lib/consent.js:91` (getConsent) and `:102`
|
||||
(onConsentChange). One env-var-naming flag from ξ that the
|
||||
post-correction also resolves: ξ used `VITE_AMPLITUDE_API_KEY` in
|
||||
code (Vite prefix convention) but the brief specified
|
||||
`AMPLITUDE_API_KEY` in the CHANGELOG MUST-step; the overlay-
|
||||
binding correction made the env-var-naming inconsistency moot.
|
||||
|
||||
Driver post-correction (on the same branch, as a follow-up commit
|
||||
`6cfbf69` — no amend of pushed commits):
|
||||
- Package: `npm uninstall @amplitude/analytics-browser && npm
|
||||
install @amplitude/unified` (resolved to `^1.1.9`).
|
||||
- Init call: `mod.init(KEY, undefined, { defaultTracking: false
|
||||
}).promise` → `mod.initAll(KEY, { analytics: { autocapture:
|
||||
true }, sessionReplay: { sampleRate: 1 } })` with a defensive
|
||||
`if (ret && ret.promise) await ret.promise` for unified-build
|
||||
variance.
|
||||
- CHANGELOG MUST step: secret-set → `flotilla overlay set
|
||||
ohm-rfc-app VITE_AMPLITUDE_API_KEY=<key>`. New Caveat sections
|
||||
spelled out the binding choice + the session-replay scope
|
||||
(with the separate-consent-category future-work flagged as
|
||||
§19.2).
|
||||
- `frontend/.env.example`: VITE_AMPLITUDE_API_KEY block rewritten
|
||||
to describe the overlay binding.
|
||||
- Wrapper header comment + warning text updated to reference
|
||||
unified + overlay.
|
||||
- Wrapper structure, event taxonomy, queue + drain, consent
|
||||
integration, anonymize-on-sign-out: all unchanged from ξ's
|
||||
work.
|
||||
- Frontend build verified green; pushed to origin + benstull
|
||||
at `6cfbf69`.
|
||||
|
||||
### Arc 5 — Sessions ο, then ν return
|
||||
|
||||
**ο** completed at `41b0c6a` on
|
||||
`feature/v0.17.0-admin-create-user`. 33 backend tests total in
|
||||
ο's surface (15 new in `test_admin_create_user_invite_vertical.py`).
|
||||
Notable decisions: opaque DB token (256-bit CSPRNG, bcrypt-at-
|
||||
rest, 7-day TTL constant `INVITE_TOKEN_TTL_DAYS = 7`); immediate-
|
||||
send (no admin-review queue); no bulk-invite (deferred); OTC
|
||||
skipped on first sign-in (token = proof of email control); no
|
||||
`users` table changes (the brief floated `first_sign_in_at IS
|
||||
NULL` as the discriminator, but the existing
|
||||
`users.last_seen_at` is NOT NULL with `datetime('now')` default
|
||||
+ no `first_sign_in_at` column existed — so the discriminator
|
||||
became "active row in `user_invite_tokens` joined on
|
||||
invited_user_id" instead, and `/api/admin/users` carries a
|
||||
new `pending_invite` field via that join); admin self-grant
|
||||
refusals (422 self-invite, 409 duplicate, 422 non-owner granting
|
||||
owner); new `permission_events` event_kind `'user_invited'` for
|
||||
the audit trail; `/api/invites/claim` lives in `main.py`'s
|
||||
oauth_router (shares `_set_device_trust_cookie` helper with
|
||||
`/auth/otc/verify`); inviter display name reads off the DB row
|
||||
fresh (not from the session-cookie cache). Admin.jsx touched
|
||||
additively only — button + modal + badge + listing-field
|
||||
consumption; existing component shape preserved.
|
||||
|
||||
**ν** completed last (~30 min vs ο's ~19 and ξ's ~12) at
|
||||
`a51beec` on `feature/v0.16.0-owner-invite`. 237 backend tests
|
||||
total in ν's surface (18 new in `test_rfc_invitations_vertical.py`;
|
||||
6 existing tests in test_pr_flow / test_graduation / test_e2e_smoke
|
||||
opted into the new contract via a new `grant_rfc_collaborator`
|
||||
helper). Notable: per-RFC roles are `contributor` (PR + discussion)
|
||||
or `discussant` (discussion only); re-accept that would lower the
|
||||
role is a no-op; the per-RFC gate falls through to the v0.6.0
|
||||
platform-granted contract when an RFC has no frontmatter owners
|
||||
yet (super-draft pre-§13.1 claim); accept endpoint requires the
|
||||
accepting user's email to match the invitee's
|
||||
`invitee_email` case-insensitively; platform-grant decision stays
|
||||
the admin's call (accepting a per-RFC invite while pending
|
||||
surfaces on `/api/admin/users` as the `rfc_invitations` array per
|
||||
user — informing, not deciding, the platform grant); email
|
||||
dispatch reuses `EmailConfig.from_env()` + `_SENT` like
|
||||
`email_otc.py`; send failure does NOT roll back the invite row
|
||||
(owner has the token on the listing surface for an out-of-band
|
||||
share); regex email validation (`^[^\s@]+@[^\s@]+$`) instead of
|
||||
pydantic `EmailStr` to avoid pulling in `email-validator`,
|
||||
matching the v0.7.0 OTC body's shape. **ν did NOT touch
|
||||
Admin.jsx directly** — the `/api/admin/users` response gained an
|
||||
additive `rfc_invitations` array (forward-compat for the existing
|
||||
UI), so the Admin.jsx-collision risk with ο that the bedtime
|
||||
prompt flagged did not materialize. The conflict moved to
|
||||
`backend/app/api_admin.py` (both ν and ο extended different
|
||||
response fields + ο added new endpoints) — additive in different
|
||||
scopes, hand-reconcilable.
|
||||
|
||||
### Arc 6 — Integration, with #21 Part C wiring folded inline
|
||||
|
||||
The driver squash-merged each branch into rfc-app `main` in
|
||||
version order, hand-resolving conflicts and adding inline
|
||||
Amplitude wiring per #21 Part C "ships inline in Session L":
|
||||
|
||||
**v0.15.0 integration** — fast-forward squash from `b3f1b15`
|
||||
(no conflicts: main was at ξ's base). Driver-side extensions
|
||||
folded in BEFORE the release commit:
|
||||
|
||||
- `analytics.js`: `identify(...)` signature extended to
|
||||
`{ user_id, properties? }` — properties apply via a new
|
||||
internal `applyProperties(props)` that builds an Amplitude
|
||||
`Identify` event with `.set(k, v)` semantics by default and
|
||||
`.setOnce(k, v)` semantics when the value is wrapped as
|
||||
`['__setOnce__', v]` (the sentinel pattern). New public
|
||||
`setUserProperties(properties)` API exposed the same
|
||||
property-apply path for mid-session state changes.
|
||||
`anonymize()` now clears `_pendingProperties` too so a
|
||||
fresh sign-in doesn't carry-over the previous user's
|
||||
property cache.
|
||||
- `App.jsx`: identify call extended to pass a viewer-derived
|
||||
property bag — `role` / `permission_state` / `passcode_set` /
|
||||
`device_trusted` (mutable `.set()`), `first_sign_in_at` /
|
||||
`account_created_at` (immutable `.setOnce()`). PII discipline
|
||||
preserved: no email, no display_name, no gitea_login.
|
||||
- CHANGELOG opening summary, Added wrapper bullet, and Added
|
||||
user-binding bullet rewritten to reflect the extensions.
|
||||
- Committed as `72f8457` "Release 0.15.0: Amplitude Analytics
|
||||
+ Session Replay (with #21 Part C identity lifecycle)";
|
||||
tag `v0.15.0` pushed origin + benstull.
|
||||
|
||||
**v0.16.0 integration** — conflicts in VERSION, package.json,
|
||||
CHANGELOG. CHANGELOG resolved by reordering to strict-descending
|
||||
(0.16.0 above 0.15.0 above 0.14.0) via a small Python script
|
||||
that read the conflict markers and emitted the incoming-block-
|
||||
first variant. Inline Amplitude wiring per #21 Part C:
|
||||
|
||||
- EVENTS taxonomy in analytics.js extended with
|
||||
`INVITATION_SENT: 'Invitation Sent'` + `INVITATION_ACCEPTED:
|
||||
'Invitation Accepted'` (plus `USER_INVITED` + `INVITE_CLAIMED`
|
||||
pre-added for v0.17.0).
|
||||
- `InvitationsModal.jsx`: fires
|
||||
`track(EVENTS.INVITATION_SENT, { rfc_slug, role_in_rfc })`
|
||||
on send success.
|
||||
- `AcceptInvitation.jsx`: fires
|
||||
`identify({ user_id, properties: { invited_at (setOnce),
|
||||
last_invited_to_rfc, last_invite_role_in_rfc, claim_method:
|
||||
'rfc-invite' } })` BEFORE
|
||||
`track(EVENTS.INVITATION_ACCEPTED, { rfc_slug, role_in_rfc })`
|
||||
on accept success.
|
||||
- CHANGELOG Added section extended with an "Amplitude wiring"
|
||||
bullet documenting the calls + the no-PII discipline.
|
||||
- Committed as `ee4925b` "Release 0.16.0: owner-only invite for
|
||||
per-RFC contribution + discussion (+ #21 Part C Amplitude
|
||||
wiring)"; tag `v0.16.0` pushed.
|
||||
|
||||
**v0.17.0 integration** — conflicts in VERSION, package.json,
|
||||
CHANGELOG, App.jsx (two regions — both imports + both routes
|
||||
kept), `backend/app/api_admin.py` (two regions — both per-user
|
||||
additive fields + ο's new endpoints all kept, resolved by a
|
||||
small Python script that emits head-block then incoming-block).
|
||||
Full backend pytest run post-merge: **252 tests pass** (matches
|
||||
the union of ν's 237 + ο's 15 new). Inline Amplitude wiring per
|
||||
#21 Part C:
|
||||
|
||||
- `Admin.jsx` `CreateUserInviteModal`: fires
|
||||
`track(EVENTS.USER_INVITED, { target_user_id, initial_role,
|
||||
custom_message_chars })` on `POST /api/admin/users` success.
|
||||
`custom_message_chars` is a coarse admin-effort signal
|
||||
(0 = template-only, 1+ = personalized).
|
||||
- `InviteClaim.jsx`: fires
|
||||
`identify({ user_id, properties: { claim_method:
|
||||
'admin-invite', invited_at (setOnce), invited_by_admin_id
|
||||
(setOnce), initial_role (setOnce) } })` BEFORE
|
||||
`track(EVENTS.INVITE_CLAIMED, { invited_by_admin_id,
|
||||
initial_role, needs_passcode, trust_device })` on
|
||||
`POST /api/invites/claim` success — so the Amplitude user
|
||||
record is created with the OHM user_id from the very first
|
||||
event the invitee fires, never as an anonymous device that
|
||||
retroactively links.
|
||||
- Committed as `1456c8b` "Release 0.17.0: admin-create user +
|
||||
invite email (+ #21 Part C Amplitude wiring)"; tag `v0.17.0`
|
||||
pushed.
|
||||
|
||||
### Arc 7 — Three serialized deploys to OHM
|
||||
|
||||
Per the §8.3 single-in-flight-deploy rule and Session K's
|
||||
serialization lesson, each version deployed in turn:
|
||||
|
||||
- **Overlay set**: `flotilla overlay set ohm-rfc-app
|
||||
VITE_AMPLITUDE_API_KEY=…` ran first. Confirmed via
|
||||
`flotilla overlay show ohm-rfc-app` — the key joined
|
||||
`VITE_TURNSTILE_SITE_KEY` and the other non-secret overlay
|
||||
values; all five Secret-Manager refs unchanged.
|
||||
- **v0.15.0**: pin bumped, committed `c784107`, pushed.
|
||||
`flotilla deploy run ohm-rfc-app` → `deploys.id=19`, all 9
|
||||
phases green, verify 6.9s. `/api/health` returns
|
||||
`{"version":"0.15.0","status":"ok"}`. Side-effect features
|
||||
v0.13.0 (cookie banner) + v0.14.0 (`/docs` route) now also
|
||||
on production for the first time since the pin had rolled
|
||||
back during Session K's Wave-4 sequence.
|
||||
- **v0.16.0**: pin bumped, committed `1c2bb26`, pushed.
|
||||
`flotilla deploy run ohm-rfc-app` → `deploys.id=20`, all 9
|
||||
phases green, verify 2.4s. `/api/health` returns
|
||||
`{"version":"0.16.0","status":"ok"}`.
|
||||
- **v0.17.0**: pin bumped, committed `dc7a572`, pushed.
|
||||
`flotilla deploy run ohm-rfc-app` → `deploys.id=21`, all 9
|
||||
phases green, verify 2.4s. `/api/health` returns
|
||||
`{"version":"0.17.0","status":"ok"}`.
|
||||
|
||||
No deploy aborts, no rollbacks, no surprises. The "stop on first
|
||||
deploy failure" Wave constraint never engaged.
|
||||
|
||||
### Arc 8 — ROADMAP strikethroughs
|
||||
|
||||
`ohm-rfc/ROADMAP.md` rows for #12, #13, #16 struck through with
|
||||
release-tag links + `deploys.id` references. #13's note records
|
||||
the mid-session binding correction ("originally said `secret
|
||||
set AMPLITUDE_API_KEY`; settled as `overlay set
|
||||
VITE_AMPLITUDE_API_KEY`") so the historical record is honest
|
||||
about the pre-vendor wording. Committed at `47cb2f9`, pushed.
|
||||
|
||||
### Arc 9 — Protocol amendment: subagent transcripts (Session-L-introduced)
|
||||
|
||||
After the driver had written what it thought was the final
|
||||
Session L transcript, the operator surfaced a series of follow-up
|
||||
asks:
|
||||
|
||||
1. "Prompt for next session pls?" + "and update transcript" —
|
||||
straightforward; the next-session prompt was already at the
|
||||
bottom of the transcript file, and the driver surfaced it as
|
||||
a copy-pasteable block in the conversation.
|
||||
2. "Do the forked agents get transcripts captured? If not, let's
|
||||
make sure they write their own transcripts out before
|
||||
returning to the main session. They can be e.g. SESSION-L.1
|
||||
etc so it's clear they're a subsession."
|
||||
3. "And the call to the agent should tell it what its transcript
|
||||
ID is."
|
||||
4. "And then update the transcript again — always do that as a
|
||||
final step of a session."
|
||||
|
||||
The first two are a protocol amendment. The current
|
||||
SESSION-PROTOCOL.md §3 said "Driver-only writes" — subagents
|
||||
returned report-backs that the driver folded into the single
|
||||
session transcript. That shape captures the high-level shape of
|
||||
each subagent's work but flattens the detail (mid-flight
|
||||
decisions, dead ends, schema choices, micro-corrections,
|
||||
test-suite outcomes). The amendment introduces subsession
|
||||
transcripts:
|
||||
|
||||
- **Naming**: `SESSION-<letter>.<n>-TRANSCRIPT-<start>--<end>.md`
|
||||
(e.g. `SESSION-M.1-TRANSCRIPT-…md` for the first subagent in
|
||||
Session M). Nested ordinals (`.1.1`) supported if a forked
|
||||
subagent itself dispatches another.
|
||||
- **Dispatch contract**: the driver pre-assigns subsession IDs
|
||||
in dispatch order, names the exact filename + the
|
||||
before-return write requirement + the transcript skeleton in
|
||||
each subagent's prompt.
|
||||
- **Driver responsibility**: main transcript references each
|
||||
subsession by filename in the cut-state table; driver
|
||||
publishes its own + each subsession's transcript via the
|
||||
publish script. Stub-write on subagent's behalf if the
|
||||
subagent crashed without producing one.
|
||||
- **Applies from Session M onward.** Session L itself ran under
|
||||
the prior "driver-only" rule because the amendment landed
|
||||
mid-session after L's three subagents had already returned;
|
||||
no `SESSION-L.1` / `.2` / `.3` transcript files exist or will
|
||||
be created retroactively.
|
||||
|
||||
Changes landed:
|
||||
|
||||
- `~/git/ohm-infra/SESSION-PROTOCOL.md` — new §5 "Subagent
|
||||
transcripts (Session-L amendment)" inserted before what was
|
||||
§5 (now renumbered §6 "When the protocol is unclear").
|
||||
- `~/git/ohm-infra/scripts/publish-transcript.sh` — filename
|
||||
validator regex extended from `[A-Za-z]+` to
|
||||
`[A-Za-z]+(\.[0-9]+)*` so subsession filenames pass.
|
||||
Dry-run validation confirmed: the parent filename still
|
||||
validates, and a hypothetical `SESSION-M.1-TRANSCRIPT-…md`
|
||||
would also pass.
|
||||
- Session L's next-session prompt (below) updated with the new
|
||||
dispatch shape so Session M's driver inherits it.
|
||||
|
||||
The fourth ask — "update the transcript again — always do that
|
||||
as a final step of a session" — was captured as a feedback
|
||||
memory at
|
||||
`~/.claude/projects/.../memory/feedback_transcript_is_final_step.md`
|
||||
so future driver sessions honor the "transcript is the LAST
|
||||
thing before publish" discipline. Memory index updated.
|
||||
|
||||
### Arc 10 — Final transcript pass + publish
|
||||
|
||||
This file. Final pass folds in arcs 1–9 + this arc; cut-state
|
||||
reflects the protocol amendment + the publish-script edit + the
|
||||
memory captured. Published via
|
||||
`~/git/ohm-infra/scripts/publish-transcript.sh` to
|
||||
`wiggleverse/ohm-session-history`.
|
||||
|
||||
---
|
||||
|
||||
## Cut state (end of session)
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| rfc-app | `main` at `1456c8b` (Release 0.17.0). Tags through v0.17.0. Last migration on disk: `019_user_invite_tokens.sql`. 252 backend tests pass. |
|
||||
| ohm-rfc | `main` at `47cb2f9` (ROADMAP strike + pin at 0.17.0). `.rfc-app-version` = `0.17.0`. |
|
||||
| OHM live | `deploys.id=21`, v0.17.0, healthy. `/api/health` = `{"version":"0.17.0","status":"ok"}`. Cookie banner (v0.13.0), `/docs` route (v0.14.0), Amplitude analytics + session replay (v0.15.0), per-RFC owner invites (v0.16.0), admin-create user + invite (v0.17.0) all live. |
|
||||
| flotilla overlay | `VITE_AMPLITUDE_API_KEY` bound (public). All other overlay / secret bindings unchanged. |
|
||||
| ohm-rfc-app-flotilla | `main` at `65c0e55`. Unchanged this session. |
|
||||
| ohm-infra (local) | `SESSION-PROTOCOL.md` amended (new §5 "Subagent transcripts"; §5 → §6); `scripts/publish-transcript.sh` regex extended to accept `SESSION-<letter>.<n>-TRANSCRIPT-…md` subsession form. |
|
||||
| Auto-memory | New feedback memory: `feedback_transcript_is_final_step.md` (transcript is the LAST step before publish). MEMORY.md index updated. |
|
||||
|
||||
| Wave 5 ledger | Status |
|
||||
| --- | --- |
|
||||
| #13 Amplitude (v0.15.0) | ✅ shipped, `deploys.id=19` |
|
||||
| #12 Owner invite (v0.16.0) | ✅ shipped, `deploys.id=20` |
|
||||
| #16 Admin-create user + invite (v0.17.0) | ✅ shipped, `deploys.id=21` |
|
||||
|
||||
| New roadmap items captured (Session L) | Status |
|
||||
| --- | --- |
|
||||
| #20 Email deliverability | 📝 captured, awaiting session |
|
||||
| #21 Amplitude audit + standing discipline | 📝 captured. Part C identity-lifecycle work shipped inline in Session L's v0.15.0 + v0.16.0 + v0.17.0 commits. Part A audit waits for ~1 week of real Amplitude data; Part B discipline can start anytime. |
|
||||
| #22 Pro-analytics-consent copy on privacy/cookies opt-in | 📝 captured. Waiting on operator-drafted + counsel-reviewed copy. |
|
||||
|
||||
---
|
||||
|
||||
## §19.2 candidates surfaced
|
||||
|
||||
1. **`@amplitude/unified` bundle size + lazy import effectiveness**
|
||||
— the unified package adds ~150 KB gzipped (vs. ~30 KB for
|
||||
analytics-browser only); the wrapper's consent-gated `await
|
||||
import('@amplitude/unified')` keeps it off the initial bundle
|
||||
for users who haven't opted in, but the post-consent init
|
||||
path hasn't been measured for jank. Part of #21 Part A's
|
||||
bundle + performance check.
|
||||
2. **Session-replay-specific consent category** — v0.13.0's
|
||||
cookie banner has a single "analytics" toggle that gates both
|
||||
events and full-DOM session recording. Recording has a
|
||||
meaningfully larger privacy footprint than event counters;
|
||||
splitting the consent into "analytics" vs "session replay"
|
||||
categories is the cleaner shape. Captured in #21's Part A +
|
||||
§19.2 follow-up.
|
||||
3. **CHANGELOG `### Migration` block has no machine-readable
|
||||
shape** — three releases in this session said "auto-applied
|
||||
on deploy by the existing migration runner" with prose;
|
||||
nothing pins which slot was claimed or surfaces a conflict
|
||||
if two parallel features claim the same slot. The slot
|
||||
coordination this session (016 reserved, 018 → #12, 019 →
|
||||
#16) was done by the driver's brief, not enforced by tooling.
|
||||
Worth a small lint or CI step.
|
||||
4. **Bedtime prompt drift vs. live state** — the bedtime prompt
|
||||
said "OHM serves v0.12.0" which was accurate, but did NOT
|
||||
mention that v0.13.0 and v0.14.0 features (cookie banner,
|
||||
`/docs`) were integrated on `main` but absent from the live
|
||||
deploy because the pin had rolled back during Session K.
|
||||
Any Wave-5 deploy was always going to bring those features
|
||||
back; the driver flagged this in arc 1 + handled it
|
||||
transparently, but a session-bootstrap script that emits
|
||||
"what is on main but not deployed?" would catch this kind of
|
||||
drift before it surprises a future driver. (This is adjacent
|
||||
to the Session-I §19.2 candidate around deploy-snapshot
|
||||
surfacing.)
|
||||
5. **Vendor-prompt-as-truth pattern** — Amplitude's installation
|
||||
wizard is an LLM-targeted prompt the vendor publishes for
|
||||
their customers to drop into Cursor / Claude Code / etc.
|
||||
When the operator pastes it mid-session, the prompt's
|
||||
recommendations override the roadmap row's pre-vendor
|
||||
guesses (package name, init shape, secret-vs-overlay
|
||||
framing). For future vendor integrations (Stripe? Linear?
|
||||
Sentry?), a discipline is worth naming: vendor-prompt
|
||||
guidance is authoritative for SDK-shape decisions, but
|
||||
integration points (consent, identity, PII) stay project-
|
||||
sovereign. Could become a §19.2 candidate spec section.
|
||||
6. **Subagent re-direction without `SendMessage`** — when the
|
||||
operator's mid-session input invalidated ξ's pre-vendor
|
||||
brief, the driver had no `SendMessage` tool available to
|
||||
redirect the in-flight subagent. The chosen path
|
||||
(let-finish, driver-post-corrects) worked here because the
|
||||
correction was small and on a fresh-cut branch; in a larger
|
||||
correction it would waste subagent work or force a
|
||||
`TaskStop` + re-dispatch. Worth exploring whether the
|
||||
harness can expose `SendMessage` for background agents, or
|
||||
whether the dispatch pattern should pre-commit to "wait for
|
||||
green-light from the operator" before doing the more
|
||||
invasive work.
|
||||
|
||||
---
|
||||
|
||||
## What lands on the operator's plate
|
||||
|
||||
1. **Amplitude dashboard verification** — vendor's installation
|
||||
prompt says "tell the user to start the application and fire
|
||||
an event to verify it is being sent to Amplitude." The
|
||||
deploy + `/api/health` verification confirmed the version is
|
||||
on the box; the **Amplitude-side confirmation that events
|
||||
actually arrive** is the operator's gesture (you have the
|
||||
dashboard login; the driver does not). Easiest probe per the
|
||||
v0.15.0 CHANGELOG: open `https://ohm.wiggleverse.org` in an
|
||||
Incognito window, accept analytics on the cookie banner,
|
||||
navigate to an RFC, watch the project's live event stream +
|
||||
session-replay panel. Expected events: `Page Viewed` (path),
|
||||
`RFC Viewed` (rfc_slug, rfc_id), the autocapture-generated
|
||||
click events on header links.
|
||||
2. **Send a real invite (#16) end-to-end** — the live
|
||||
`/admin/users` surface now has the "Create user + invite"
|
||||
affordance. A real-world deliverability test is part of the
|
||||
#20 input data: send to a gmail address, a yahoo address, a
|
||||
protonmail address, watch which ones land in inbox vs. spam.
|
||||
3. **Send a real per-RFC invitation (#12)** — same shape; pick
|
||||
an RFC, hit "Invitations" in the header strip, send to your
|
||||
alternate email.
|
||||
4. **Roadmap items #20 / #22 are operator-led**:
|
||||
- **#20** needs DNS edits on `wiggleverse.org` (SPF / DKIM /
|
||||
DMARC) and SMTP-provider dashboard work. Subagent can
|
||||
inventory + propose; the executions are operator-only.
|
||||
- **#22** needs operator-drafted copy + counsel review
|
||||
before a subagent wires it. The roadmap item spells out
|
||||
the legal caveats (501(c)(3) is planned-not-granted, "no
|
||||
external sharing" honesty about Amplitude as the
|
||||
processor, purpose-limitation enforceability,
|
||||
GDPR/CCPA coerced-consent risk).
|
||||
5. **§19.2-candidate #6 (subagent re-direction)** is a
|
||||
harness-feature request worth raising with the Claude Code
|
||||
team if it bites again.
|
||||
6. **Item #1 VM rename + #17 inventory + #19 PR coordination**
|
||||
stay operator territory.
|
||||
|
||||
---
|
||||
|
||||
## Updated next-session prompt
|
||||
|
||||
```
|
||||
You are the OHM roadmap driver. The previous session (Session L)
|
||||
shipped rfc-app v0.15.0 (Amplitude Analytics + Session Replay,
|
||||
#13, deploys.id=19), v0.16.0 (per-RFC owner invites, #12,
|
||||
deploys.id=20), and v0.17.0 (admin-create user + invite email,
|
||||
#16, deploys.id=21) to OHM. All of Wave 5 is closed. OHM serves
|
||||
v0.17.0 with Amplitude analytics + session replay live (gated by
|
||||
the v0.13.0 cookie banner), v0.13.0 cookie banner itself, v0.14.0
|
||||
/docs route, per-RFC owner invites, and admin-create user invites
|
||||
all in production for the first time. Session L also folded #21
|
||||
Part C identity-lifecycle work inline across all three release
|
||||
commits — identify({ user_id, properties }) + setUserProperties()
|
||||
+ amplitude.reset on sign-out + identify-BEFORE-track on every
|
||||
invite-claim path.
|
||||
|
||||
Read `~/git/ohm-infra/SESSION-PROTOCOL.md` and
|
||||
`/Users/benstull/projects/wiggleverse/ohm-rfc/ROADMAP.md`
|
||||
end-to-end. The next session letter is M.
|
||||
|
||||
**New protocol amendment from Session L**: subagents write their
|
||||
own transcripts (subsession files like
|
||||
`SESSION-M.1-TRANSCRIPT-<start>--<end>.md`) before returning to
|
||||
the driver. The driver pre-assigns each subagent's subsession ID
|
||||
in the dispatch prompt. The driver's own Session-M transcript
|
||||
stays load-bearing for cross-subagent synthesis + cut-state,
|
||||
with cross-references to each subsession. See
|
||||
SESSION-PROTOCOL.md §5 for the binding form. The publish
|
||||
script's filename validator was extended in Session L to accept
|
||||
`SESSION-<letter>.<n>-TRANSCRIPT-…md`.
|
||||
|
||||
**Hard rule (Session K + Session L): never EVER ask the operator
|
||||
to paste secret bytes into the conversation.** Session L
|
||||
confirmed the corollary: public bundle-embedded values
|
||||
(`VITE_AMPLITUDE_API_KEY`, `VITE_TURNSTILE_SITE_KEY`) are fine
|
||||
to receive in conversation and set via `flotilla overlay set` —
|
||||
but never the secret-half of a public/secret pair, and never
|
||||
something whose nature you're unsure about. When in doubt,
|
||||
default to the operator-run gesture (`pbpaste | flotilla secret
|
||||
set …` for secrets; `flotilla overlay set DEPLOYMENT KEY=VALUE`
|
||||
for non-secrets).
|
||||
|
||||
Wave 6 candidates (per Session L's roadmap state):
|
||||
- **#20 Email deliverability** (Track Ω + rfc-app template
|
||||
hygiene) — high priority now that two invite paths ship and
|
||||
the recipient's first contact with OHM is an email; spam
|
||||
foldering is silent invite failure. Likely one combined ops
|
||||
session for DNS + SMTP-provider posture + template hygiene
|
||||
(note: overlaps with #18; ship together).
|
||||
- **#22 Pro-analytics-consent copy on the privacy/cookies
|
||||
opt-in** — small frontend-only rfc-app minor; depends on
|
||||
operator-drafted + counsel-reviewed copy in hand. Legal
|
||||
caveats spelled out in the item (501(c)(3) is planned, not
|
||||
granted; "no external sharing" honesty about Amplitude as the
|
||||
processor; purpose-limitation pledges are enforceable;
|
||||
GDPR/CCPA coerced-consent risk; withdrawal must stay easy).
|
||||
- **#21 Part A audit** — waits for ~1 week of real Amplitude
|
||||
data, then a deep audit pass on taxonomy, autocapture quality
|
||||
(DOM hygiene), session-replay masking, bundle/performance.
|
||||
Ships as a rfc-app minor.
|
||||
- **#21 Part B discipline** — SPEC.md analytics chapter +
|
||||
rfc-app CONTRIBUTING checklist + optional CI lint. Can start
|
||||
anytime; ships with #19's CONTRIBUTING PRs.
|
||||
- **#18 Secure SMTP relay + Gitea webhook** — likely bundled
|
||||
with #20.
|
||||
- **#17 Repo naming + location alignment** — operator-led; a
|
||||
subagent can inventory + propose, operator executes.
|
||||
- **#19 CONTRIBUTING guides + transcript-linked onboarding** —
|
||||
Track Ω docs PR; subagent can draft, operator approves +
|
||||
merges.
|
||||
- **#1 VM rename** — operator-led ops gesture.
|
||||
|
||||
Dispatch shape (updated for Session M per the Session-L
|
||||
amendment):
|
||||
1. For each subagent, pre-assign a subsession ID: M.1, M.2, …
|
||||
2. In the dispatch prompt, name the subsession ID + tell the
|
||||
subagent it MUST write `~/git/ohm-infra/SESSION-M.<n>-TRANSCRIPT-<start>--<end>.md`
|
||||
BEFORE returning a report.
|
||||
3. The subagent's transcript follows the same shape as a main
|
||||
transcript (pre-state, arcs, cut-state, what-the-driver-
|
||||
needs). The driver's report-back is a tight summary; the full
|
||||
detail lives in the subagent's transcript file.
|
||||
4. Each subagent creates its own `git worktree add` in
|
||||
`/Users/benstull/git/rfc-app-…`, pushes a feature branch,
|
||||
does NOT tag, does NOT touch the ohm-rfc pin, does NOT run
|
||||
flotilla deploy.
|
||||
5. Driver integrates serially.
|
||||
6. Driver publishes its own transcript AND each subsession
|
||||
transcript via `~/git/ohm-infra/scripts/publish-transcript.sh`
|
||||
(the regex now accepts `SESSION-<letter>.<n>-TRANSCRIPT-…md`).
|
||||
|
||||
Session-L lessons to apply automatically (in addition to all of
|
||||
Session K's):
|
||||
- **Vendor prompts can override the roadmap's pre-vendor
|
||||
guesses.** When the operator pastes a vendor installation
|
||||
prompt mid-session (Amplitude this time; could be Stripe,
|
||||
Linear, Sentry next time), the prompt's SDK-shape
|
||||
recommendations are authoritative for the SDK choices —
|
||||
package name, init signature, sample rates, etc. The roadmap
|
||||
row's "new secret: FOO" or "uses bar library" wording was
|
||||
best-effort pre-vendor and gets superseded. Integration
|
||||
points (consent gates, identity lifecycle, PII discipline)
|
||||
stay project-sovereign — the vendor doesn't get to decide
|
||||
those. Document the override in the release CHANGELOG +
|
||||
strike the ROADMAP row with the corrected framing.
|
||||
- **No `SendMessage` available in this harness.** When the
|
||||
operator's mid-session input invalidates an in-flight
|
||||
subagent's brief, the choices are (a) let it finish and
|
||||
driver-post-correct, (b) `TaskStop` and re-dispatch. Pick (a)
|
||||
when the correction is small and on a fresh-cut branch; (b)
|
||||
when the structural decisions would be wrong. The
|
||||
driver-post-correction is a new commit on the same branch
|
||||
(no amend, no force-push), pushed to both remotes.
|
||||
- **Squash-merge integration with hand-resolved CHANGELOG**
|
||||
works well for parallel-feature waves. Use a small Python
|
||||
script for the conflict resolution when the block is large
|
||||
(~150+ lines) — git's conflict markers are accurate but
|
||||
manual editing is error-prone at scale.
|
||||
- **#21 Part C is a standing pattern**: every new
|
||||
identity-meaningful surface MUST call `identify({ user_id,
|
||||
properties })` (not just `setUserId`), and any new claim/
|
||||
sign-in/invite-accept path MUST call identify BEFORE the
|
||||
first track event so the Amplitude record is created with
|
||||
the OHM user_id from the first event rather than as an
|
||||
anonymous device that retroactively links. SPEC chapter to
|
||||
codify is part of #21 Part B.
|
||||
- **Pin-jump side effects**: when the pin bumps past versions
|
||||
that were integrated-but-undeployed (in Session L: v0.13.0
|
||||
cookie banner + v0.14.0 /docs both went live as a side
|
||||
effect of the v0.15.0 → v0.17.0 sequence), flag this to the
|
||||
operator at the deploy step. Not a blocker; just transparency.
|
||||
- **CHANGELOG slot 016 stays reserved + skipped**. v0.15.0
|
||||
used no schema migration; #12 took slot 018, #16 took 019.
|
||||
Next free: 016, 020, 021, ….
|
||||
- **Login.jsx remains contested**: Session L's v0.15.0
|
||||
Amplitude wiring added `User Signed In` calls at the three
|
||||
verify paths (otc / passcode / trust-device). Future
|
||||
auth-touching releases need to preserve v0.11.0 + v0.12.0 +
|
||||
v0.15.0 injections.
|
||||
- **Admin.jsx contested too**: v0.15.0 added Admin Permission
|
||||
Decision firing; v0.17.0 added Create-user-invite modal +
|
||||
pending-invite badge. Future admin-page work layers on top
|
||||
additively.
|
||||
- **api_admin.py contested**: v0.16.0 added `rfc_invitations[]`
|
||||
per-user; v0.17.0 added `pending_invite` per-user + new
|
||||
endpoints. Both additive; the per-user dict has two new
|
||||
fields side-by-side. Future admin-API work should follow
|
||||
the same additive shape and not restructure the response
|
||||
envelope.
|
||||
- **Roadmap items added mid-session**: #20 email deliverability,
|
||||
#21 Amplitude audit + standing discipline (Parts A + B + C),
|
||||
#22 pro-analytics-consent copy on the privacy/cookies opt-in.
|
||||
All three pushed to ohm-rfc as their own commits during the
|
||||
session. Don't batch roadmap captures to end-of-session — push
|
||||
promptly so the operator sees them on the gitea side without
|
||||
waiting.
|
||||
|
||||
End-of-session: write `~/git/ohm-infra/SESSION-M-TRANSCRIPT-…md`
|
||||
(your main transcript) AND ensure each subagent's subsession
|
||||
transcript was written. Per Session-L feedback: **transcript is
|
||||
the LAST step** — finish all other work first, then finalize the
|
||||
transcript, then publish all of them via
|
||||
`~/git/ohm-infra/scripts/publish-transcript.sh`. If the operator
|
||||
surfaces new instructions after the transcript is "finalized,"
|
||||
it isn't yet final — apply the new work, finalize again, then
|
||||
publish.
|
||||
```
|
||||
Reference in New Issue
Block a user