add ohm/0075/SESSION-0075.0-TRANSCRIPT-2026-06-05T12-45--2026-06-05T13-21.md + replace placeholder/variant SESSION-0075.0-TRANSCRIPT-2026-06-05T12-45--INPROGRESS.md

This commit is contained in:
2026-06-05 13:22:03 -07:00
parent 8630d450a8
commit 16dc249189
2 changed files with 138 additions and 23 deletions
@@ -0,0 +1,138 @@
# Session 0075.0 — Transcript
> App: ohm
> Start: 2026-06-05T12-45 (PST)
> End: 2026-06-05T13-21 (PST)
> Type: coding
> Status: **FINALIZED**
> Driver: Ben Stull (with Claude Opus 4.8)
> Outcome: **§22 slice S2 shipped to `main` as v0.41.0 (PR #20 merged).**
## Launch prompt
```
Goal: §22 S2 — next slice of the three-tier (deployment→project→collection)
refactor, per the three-tier design doc / roadmap. Continue from S1 (collection
grain, mig 029, shipped v0.40.0).
```
(`/goal S2` set a Stop-hook gate on the condition "S2".)
## Plan
**Goal: §22 S2 — Create & navigate a second collection** (per
`docs/design/2026-06-05-three-tier-projects-collections.md` Part E). Continues
S1 (collection grain, mig 029, v0.40.0). Acceptance gate `@S2` = scenario C3.6
(anonymous reader of an empty public collection catalog → empty catalog, no
propose action, sign-in prompt).
Usable end-state: an admin creates a second collection beside the default one;
it is navigable + proposable. Slice surfaces:
1. **Registry mirror** reads `.collection.yaml` manifests inside each project's
content repo (beyond the seeded default collection).
2. **create-collection endpoint** (bot-commit-wrapped; authorized by deployment
owner/admin for now — scoped roles are S3).
3. **Project collection-directory** at `/p/<project>/` (2+ collections → list;
exactly one visible → keep the S1 redirect).
4. **Collection-scoped serve + propose** under `/p/<project>/c/<collection>/`,
incl. the C3.6 anonymous empty-catalog state.
Plan doc: `docs/design/plans/2026-06-05-s2-second-collection.md`.
Release: minor bump (new functionality, non-breaking) + CHANGELOG entry.
## Pre-state
- `main` clean at `4ffff6b`, **v0.40.0** (S1 / mig 029 shipped via PR #19).
- A `collections` table exists; entries keyed `(collection_id, slug)`; one seeded
`default` collection per project. Serving was project-scoped (collection =
default); the `.collection.yaml` reader + collection-aware serving were S2.
- Backend: FastAPI + SQLite under `backend/` (`.venv`, `pytest`). Frontend: React +
react-router + vitest under `frontend/`.
## Turn-by-turn arc
1. **Session gate + claim.** Classified as a coding session; ran
`wgl-session-coding-init`, claimed ohm session **0075** (no other in-flight
sessions). Verified a clean pushed `main` baseline @ v0.40.0.
2. **Orient on S2.** Read the design doc Part E (slice S2) + the Part C scenarios.
Pinned the `@S2` acceptance to **C3.6** (anon empty public collection catalog).
3. **Map the surfaces.** An Explore subagent + direct reads mapped every S2 surface
(registry mirror, `collections.py`, `cache.refresh_meta_repo`, `bot.open_idea_pr`,
the propose helper, `api_deployment.py`, the frontend Catalog/App routes,
`api.js`, the `FakeGitea` test fixture). Discovered the anonymous empty-state was
*already* mostly present in `Catalog.jsx` — the real work was making the
catalog/entry views **collection-scoped** plus the backend mirror/endpoint.
4. **Write the plan** (`writing-plans` skill) → `docs/design/plans/2026-06-05-s2-second-collection.md`,
10 TDD tasks. Logged the lower-confidence calls (create-collection commits
straight to main; coarse S2 visibility; layout convention; S4-deferred empty
states) to the Deferred-decisions section.
5. **Execute** (`executing-plans` skill), branch `feat/s2-second-collection`, one
commit per task, TDD throughout (red → green → full-suite checkpoint):
- **T1** registry `.collection.yaml` discovery (`parse_collection_manifest`,
`_mirror_named_collections`, immutable-type + visibility-inherit).
- **T2** `collections.py` read helpers (`list_collections`/`get_collection`/
`subfolder_of`).
- **T3** corpus mirror collection-grained (`_refresh_collection_corpus`).
- **T4** collection-scoped list/get/propose; refactored the project routes into
`_list_rfcs_for_collection`/`_get_rfc_for_collection`/`_propose_into_collection`;
`bot.open_idea_pr` gained `rfcs_dir`.
- **T5** create-collection (`api_collections.py` + `bot.create_collection`),
mounted in `api.py`; extended `FakeGitea` to model directory listings so the
content-repo walk is exercised end-to-end.
- **T6** frontend `useCollectionId()` + collection-scoped `api.js` (+ a
fetch-mocked URL test).
- **T7** Catalog reads the active collection; propose modal threads it.
- **T8** `CollectionDirectory` at `/p/<project>/` (1 → redirect, 2+ → list),
replacing `DefaultCollectionRedirect`.
- **T9** `@S2` acceptance test (C3.6): anon empty public collection → 200 `[]`,
propose rejected (401).
- **T10** release v0.41.0 (VERSION + package.json + CHANGELOG + design-doc
"shipped" note).
6. **Finish the branch** (`finishing-a-development-branch`): both suites green
(backend **478**, frontend **26**, build green); pushed the branch; created **PR
#20** via the Gitea API; merged it (merge commit `39ce54f`); synced `main`,
deleted the feature branch (local + remote).
## Cut state
- `main` @ `39ce54f`, **v0.41.0**, clean and pushed. No open PRs.
- Backend **478 passed**; frontend **26 passed**; frontend production build OK.
- New files: `backend/app/api_collections.py`,
`backend/tests/test_collection_registry.py`,
`backend/tests/test_collection_helpers.py`,
`backend/tests/test_collection_scoped_serve.py`,
`backend/tests/test_collection_create_vertical.py`,
`frontend/src/components/CollectionDirectory.jsx` (+ test),
`frontend/src/api.collections.test.js`,
`docs/design/plans/2026-06-05-s2-second-collection.md`.
- Acceptance `@S2` (C3.6) green. N=1 default-collection path unchanged.
## Operator plate (decisions surfaced for review)
These were autonomous-mode calls (no clearly-wrong alternative; logged rather than
gated):
1. **create-collection commits straight to `main`** of the content repo (no PR),
then the registry mirror upserts the row. A collection is structural config and
§22.2 makes the registry the source of truth. A PR-gated variant is possible if
you want collection creation reviewed.
2. **S2 collection visibility filtering is coarse** — project read-gate + drop
`unlisted` from enumeration. Full `{owner, contributor}` scope-role enforcement
at the collection axis is **S3**.
3. **Content-repo layout** for a named collection: manifest at
`<subfolder>/.collection.yaml`, entries at `<subfolder>/rfcs/`. Default
collection (subfolder `''`) keeps the shipped repo-root `rfcs/`.
4. **Role-keyed empty-state affordances** ("Create your first collection", "Propose
the first entry") are **S4** — S2 ships only the anonymous empty catalog (C3.6)
and a minimal directory list.
## Next-session prompt
```
/goal S3 — scope-role enforcement across global/project/collection: the
four-layer most-permissive resolver over {owner, contributor} grants, every
write gate re-checked on the collection axis. Per
docs/design/2026-06-05-three-tier-projects-collections.md Part E (completes
@S3 = C1.1C1.8).
```
@@ -1,23 +0,0 @@
# Session 0075.0 — Transcript
> App: ohm
> Start: 2026-06-05T12-45 (PST)
> Type: coding
> Status: **PLACEHOLDER — claimed at session start; finalized at session end.**
>
> This file reserves session ID 0075 for ohm. The driver replaces this
> body with the full transcript and renames the file to its final
> SESSION-0075.0-TRANSCRIPT-2026-06-05T12-45--<end>.md form at session end.
## Launch prompt
```
Goal: §22 S2 — next slice of the three-tier (deployment→project→collection) refactor, per the three-tier design doc / roadmap. Continue from S1 (collection grain, mig 029, shipped v0.40.0).
```
## Deferred decisions
_Autonomous-mode low-confidence calls the driver made and would have
liked operator input on. Appended as the session runs; surfaced at
finalize. Empty if none._