8.1 KiB
Session 0074.0 — Transcript
App: ohm Start: 2026-06-05T07-35 (PST) End: 2026-06-05T08-35 (PST) Type: coding Status: FINALIZED. Branch:
feat/s1-collection-grain→ PR #19 (merged tomain). Released v0.40.0.
Launch prompt
/goalWrite + execute the S1 plan for the §22 three-tier refactor: migration 029 (addcollectionstable, move per-corpus fields down fromprojects, one default collection per project, re-key entry-scoped tables (project_id,slug)→(collection_id, slug) via the 028 rebuild pattern, project_members→memberships) + thread collection_id through app/auth.py·projects.py·cache.py·api_* + 308 /p//e/→ /p//c//e/. Acceptance: @S1 scenarios green; N=1 unchanged. Read docs/design/2026-06-05-three-tier-projects-collections.md (Part E / §A.6) first. Review/merge PR #18 first if approved.
Plan
Coding session executing slice S1 of the §22 three-tier refactor (design doc spec'd in session 0072). Write a written implementation plan, then execute it via TDD against the full backend suite as the "N=1 unchanged" regression net. Gate: review/merge the spec PR #18 first.
Pre-state
- Clean, pushed
main@ v0.39.0 (two-tier deployment→project model shipped). - The three-tier design doc was on branch
spec/three-tier-projects-collections, open as PR #18 (unmerged) ongit.wiggleverse.org/ben.stull/rfc-app. - Memory flagged PR #18 as open and "NEXT = write+execute the S1 plan."
The arc (turn-by-turn)
- Session gate / init. Classified as a coding session; claimed ID 0074
(
--type coding), no concurrent sessions, cleanmainbaseline. - PR #18 triage. The memory's "PR #18" wasn't on the
benstullremote (git.benstull.org) — found it on the canonicalorigin(git.wiggleverse.org/ben.stull/rfc-app, PR #18 open + mergeable). Reviewed the 637-line design doc in full: internally consistent, stale-memory premise corrected (two-tier shipped @ v0.39.0), migration-029 strategy decided, slices@S1-tagged. Verdict: approved → merged PR #18 tomain(the design doc is now on main; the SPEC.md merge itself rides S6). - Codebase exploration. Read migration 028 (the rebuild pattern to replicate),
026/027 (current
projects/project_membersschema;type/initial_statelive onprojects),projects.py/auth.py/cache.py/api_deployment.py/App.jsx. Three parallel Explore agents mapped theproject_idblast radius, the routing/308 surface, and the test/migration tooling (key finding: no Gherkin runner —@S1is realised as pytest verticals; the full suite is the N=1 net). - Decision: default collection id = literal
'default'. Established that a stable literal (not the project id) keeps the collection id identical across fresh-vs- upgraded deploy histories, matches the spec's/c/default/URLs, and lets the existingrestampkeep renaming only the project grain. The re-key maps each entry to its project's default collection via a JOIN (robust to project_id values). - Wrote the S1 plan (
docs/superpowers/plans/2026-06-05-s1-three-tier-collection-grain.md) via the writing-plans skill — phased: migration 029 → backend threading → API surface → redirects →@S1acceptance → release. - Executed (TDD, branch
feat/s1-collection-grain).- Migration 029 (
029_collections.sql):collectionstable;type/initial_statemoved down offprojects; one default collection per project (id='default', repo root); the 13 entry-corpus tables re-keyed(project_id,slug)→(collection_id,slug)via the 028 rebuild pattern;project_members→memberships(scope_type∈{project,collection},…), role enum collapsed to{owner,contributor}. Shape test green first. - Threading. Renaming the column broke the whole suite (the intended TDD signal).
Fixed module-by-module against the regression net:
collections.pyhelpers; the registry mirror (writes project group-fields + collection corpus-fields, type immutability on the collection);auth.project_of_rfc(collections join);auth.project_member_role(reads memberships, maps back to legacy role strings);cache/api.py/api_prs/api_branches/api_notifications/api_contributions/api_invitations/api_graduation/api_discussion/funder(13-table reads/writes →collection_id;cached_prs+ denormalisedproject_idtags left unchanged);projects.pyrestamp + initial_state;api_deploymentreads type/initial_state from the default collection. A missedproject_of_rfcedit (500 on every entry read) was the single biggest fix — caught by the suite. - Redirects. Backend
/rfc/+/proposals/308s now target/c/default/. Frontend:/c/:collectionId/route layer;DefaultCollectionRedirect(C3.7);LegacyCorpusRedirect(v0.35.0/p/<p>/e/<slug>bookmarks →/c/default/, query preserved); path builders carry the segment (serving stays project-scoped — collection=default; S2 owns collection-aware serving). - Tests. Added
test_migration_029_collections.py,test_s1_collection_grain_vertical.py(@S1),entryPaths.test.js. Updated the fixtures/assertions that seeded the dropped columns (test_api_deployment,test_registry,test_registry_wiring,test_migration_027,test_initial_state_landing,test_restamp_default_project, the multi-project + project-scoped verticals). Retiredtest_migration_028(its keying is superseded by 029; invariants now covered at the collection grain).
- Migration 029 (
- Verification + release. Backend 458 passed, frontend 18 passed, build green
(the N=1-unchanged gate +
@S1). Bumped VERSION/package.json → 0.40.0 (parity checked), wrote the CHANGELOG entry with a breaking-URL upgrade-steps block. Pushed; opened PR #19; self-reviewed (fully tested) and merged tomain; deleted the branch.
Cut state
main@ v0.40.0, clean and pushed. Migration029_collections.sqlonmain.- Slice S1 shipped: the collection grain exists as the invisible default; N=1 deployments
run unchanged with one extra
/c/<collection>/path segment. - Full backend suite 458 green; frontend 18 green + build green.
Operator plate (what shipped)
- The three-tier model (deployment → project → RFC collection) now has its collection grain
in code. A deployment upgrading 0.39.0 → 0.40.0 runs migration 029 automatically (no config
change); existing entries re-key into the default collection with no data loss; old
/p/<project>/e/<slug>,/rfc/<slug>,/proposals/<n>links keep working.
Deferred decisions
- Default collection id = literal
'default'(not the project id): chosen for cross-deploy-history stability + spec-URL fidelity; the multi-project-at-migration case uses aCASEfallback to the project id to avoid a PK collision (non-standard pre-S5). Worth a glance from the spec author, but it honors the spec's structural invariants. test_migration_028retired rather than rewritten — its(project_id,slug)keying is overwritten by 029, so the test asserted a state the runner can no longer reach; the collection-grain invariants live intest_migration_029.- Denormalised
project_idtags (cached_prs/threads/changes/notifications/actions/pr_resolution_branches) intentionally left asproject_id(project grain) for S1.
Next-session prompt
/goal Execute slice S2 of the §22 three-tier refactor (create & navigate a second
collection): teach the registry mirror to read `.collection.yaml` manifests; add the
bot-commit-wrapped create-collection endpoint (authorized by existing deployment
owner/admin for now); the project collection-directory at /p/<project>/; and
collection-scoped propose/serve under /p/<project>/c/<collection>/. Acceptance: @S2
scenario (C3.6, anonymous reader of an empty collection catalog) green; N=1 unchanged.
Read docs/design/2026-06-05-three-tier-projects-collections.md (Part E "S2") first.