# Session 0067.0 — Transcript > App: ohm (rfc-app framework) > Date: 2026-06-03T20-31 → 2026-06-04T01-55 (PST) > Goal: design **and** ship the backend half of §22 slice M3 (multi-project). > Outcome: **M3-backend Plan A merged to `main`** (PR #11, `6c2bdb3`, v0.33.0). ## Launch prompt Session-init → goal proposal selected §22 M3 (next slice after M1+M2). Operator invoked brainstorming to design it. ## Plan **Goal:** Design, plan, and implement the backend half of §22 slice **M3** — multi-project registry mirror + data spine + runtime-config APIs. Roadmap: `ohm-rfc/ROADMAP.md` defers to the framework slicing plan in `rfc-app/docs/design/multi-project-spec.md` Part C. **Brainstorming decisions:** 1. Scope split at the backend/frontend seam — this session = **M3-backend**; routing/branding/directory/switcher = a later **M3-frontend** spec. 2. Build the full `initial_state`/`unreviewed` review plumbing now. 3. Config **hard cut**: `REGISTRY_REPO` required, `META_REPO` removed. 4. Re-stamp `default` → config slug by rewriting `project_id` everywhere. 5. Registry mirror in a self-contained `app/registry.py` (Option A), driven by the existing webhook + `Reconciler.sweep()`. **Planning discoveries (folded into the spec, reshaped the plan):** - The re-stamp must be a **Python startup step** — pure-SQL migrations can't read `DEFAULT_PROJECT_ID` (same constraint that forced M1's `seed_default_project`). - The 12-table PK rebuild has a wide blast radius (~10 `ON CONFLICT` sites + every RFC/branch writer needs `project_id` threading; FK-bomb post-re-stamp). → Operator split M3-backend into **Plan A** (additive: registry mirror + APIs + review semantics + migration 027 — no rebuild/re-stamp/threading) and **Plan B** (PK rebuild + threading + re-stamp, before M4). This session executed **Plan A**. ## Pre-session state - `main` carried M1 (schema spine) + M2 (project-scoped authz) + the v0.32.0 retire/§13 work + M3-0 (Tier-1 test harness, PR #9). Latest migration `026`. - `REGISTRY_REPO` was wired into config but optional/unused; `META_REPO` was the live corpus source; `projects` table had no `type`/`initial_state`; no `deployment` table; no registry mirror; no `/api/deployment`. - Session-init found `~/.claude/wiggleverse.md` was a **dangling symlink** (digest `agent-context.md` missing; only `engineering/README.md` present) — so the CLAUDE.md import was deferred at init, then resolved externally mid-session (committed `0f6b2b4`). ## Turn-by-turn arc 1. **Init** — claimed session 0067; flagged 3 stale `--INPROGRESS` sessions; clean `main` baseline; surfaced the dangling-symlink gap; proposed M3 as next goal. 2. **Brainstorming** — explored backend + frontend + §22 spec via parallel Explore agents; settled the 5 decisions above; wrote + committed the design spec (`6f356d3`), self-reviewed, operator-approved. 3. **Writing-plans** — read the real code (config/cache/webhooks/entry/api/bot/db/ gitea/migration 026/tests). Two coupling discoveries (env-read → Python re-stamp; PK-rebuild blast radius) → operator chose **two plans, mirror first**. Wrote + committed **Plan A** (9 TDD tasks, `dd72f91`). 4. **Implementation** (subagent-driven) — 9 tasks, each: implementer → spec-compliance review → code-quality review, fixes looped to approval. Real bugs caught by review and fixed: `/api/projects/:id` 500→404 for superusers on unknown id; non-dict registry entry → `RegistryError`; `apply_registry` made atomic; hygiene `None` content_repo guard; changelog `draft`→`super-draft`. Final whole-impl review (opus): **ready to integrate**. 5. **Integrate** — pushed branch; opened PR #11; Gitea blocked self-approval (422, expected); operator-authorized merge → `main` (`6c2bdb3`); branch deleted; merged `main` re-verified green (434); memory + `MEMORY.md` index updated. 6. **Finalize** — discussed JSONL-vs-curated transcript; filed a roadmap item (plugin PR #3) to source future transcripts from the session JSONL log (with secret-byte redaction + memory fallback); published this transcript. 7. **PR cleanup** — swept all open PRs: merged **#10** (M3-frontend design spec) and **#3** (plugin roadmap) to `main`; closed **#8** (empty diff — §13/retire already on main) and **#4** (superseded by #9) as stale. rfc-app now has zero open PRs. (Gitea token had repo/code scope but not issue scope — closes went through the `/pulls` endpoint; explanatory comments were 403'd, dispositions recorded here instead.) ## Cut state (end of session) **M3-backend Plan A is merged to `main`** — PR #11, merge `6c2bdb3`, **v0.33.0**. Full suite **434 passed** on merged main. Branch `m3-backend-registry-spine` deleted (local + remote). Shipped: migration `027_projects_runtime_config.sql` (additive — `type`/ `initial_state` on `projects`, `deployment` singleton, `unreviewed`/`reviewed_*` on `cached_rfcs`); `app/registry.py` (§22.2 parse/validate/apply, atomic); webhook + `Reconciler.sweep` + startup wiring (startup-fatal / running-tolerant); hard-cut `META_REPO`→`REGISTRY_REPO` (corpus mirror reads `content_repo` from the default project row); `app/api_deployment.py` (`GET /api/deployment`, `GET /api/projects/:id`, §22.5-gated); `initial_state` honoring at propose; mark-reviewed endpoint + `?unreviewed=true` catalog filter; CHANGELOG 0.33.0 breaking upgrade block. Per-task commit trail (each + its review fixes): T1 `597f6bc` · T2 `2d9022b` · T3 `8f21dc5` · T4 `27a0a04`/`f7bd466` · T5 `cecc6c0`/`48fd6f9` · T6 `c386b05`/`07e003e`/`e8ce3cd` · T7 `fe47eef`/`2fe2a71` · T8 `76207bb`/`69fd0cb`/`8004b2a` · T9 `a2dc29a`/`f114af8`. Plus CLAUDE.md org-context import `0f6b2b4`. All on `main` via the PR #11 merge. ## What lands on the operator's plate - **M3-frontend is next** — its design spec is now **merged to `main`** (PR #10); write its implementation plan against the real `/api/deployment` + `/api/projects/:id` shapes, then execute on the M3-0 Tier-1 harness. The `feat/m3-frontend` branch was kept (not deleted) in case in-flight work continues on it. - **Deploy upgrade** (when shipping v0.33.0): create a registry repo, author `projects.yaml` (`id: default`, `content_repo` = old `META_REPO`), set `REGISTRY_REPO`, remove `META_REPO`, add the registry webhook — per CHANGELOG 0.33.0 upgrade steps. - **PRs all cleared** — rfc-app has zero open PRs (stale #8/#4 closed; #10 merged). Plugin roadmap PR #3 merged. - **Plan B** (before M4): 12-table PK rebuild (migration `028`) + `project_id` writer-threading (concrete sites: mark-reviewed + propose hardcode the default content_repo) + `default`→slug re-stamp + two-project isolation tests. - **Roadmap landed**: plugin PR #3 (merged) — generate future transcripts from the session JSONL log, with secret-byte redaction + memory fallback. ## Prompt the operator can paste into the next session ``` Start a coding session on rfc-app (~/git/wiggleverse.org/ben.stull/rfc-app). Read memory: MEMORY.md + project_rfc_app_multiproject.md. M3-backend Plan A is merged to main (v0.33.0); the runtime APIs GET /api/deployment and GET /api/projects/:id now exist. Next slice: M3-frontend — its design spec is now on main (docs/superpowers/specs/2026-06-03-m3-frontend-design.md, merged via PR #10). Write its implementation plan against those real API shapes, then execute it via subagent-driven-development on the M3-0 Tier-1 harness (Vitest + Playwright e2e). Coordinate the DEFAULT_PROJECT_ID / 308 ownership seam noted in the spec. (PR backlog is clear — no triage needed.) Plan B (PK rebuild migration 028 + project_id writer-threading + default→slug re-stamp) stays deferred until just before M4. ```