add ohm/0077/SESSION-0077.0-TRANSCRIPT-2026-06-05T23-31--2026-06-06T00-42.md + replace placeholder/variant SESSION-0077.0-TRANSCRIPT-2026-06-05T23-31--INPROGRESS.md
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# Session 0077.0 — Transcript
|
||||
|
||||
> App: ohm
|
||||
> Start: 2026-06-05T23-31 (PST) · End: 2026-06-06T00-42 (PST)
|
||||
> Type: coding
|
||||
> Goal: `/goal s4` — ship §22 three-tier slice **S4** (invitation surfaces +
|
||||
> role-aware empty states).
|
||||
> Outcome: **S4 shipped @ v0.43.0** (origin `main` = `2696e64`, PR #21 merged
|
||||
> via the Gitea API). Completes `@S4` (C2.1–C2.7 + C3.3–C3.5). Backend 504
|
||||
> passed; frontend build green. Non-breaking.
|
||||
|
||||
## Plan
|
||||
|
||||
**S4 — Invitation surfaces + role-aware empty states** (`@S4` = C2.1–C2.7,
|
||||
C3.3–C3.5), building on S3's `effective_scope_role` resolver (mig 030).
|
||||
|
||||
Backend: (1) `auth` invite gates; (2) `memberships.py` grant/revoke/list with
|
||||
the broader-scope-supersedes prune; (3) `api_memberships.py` GET/POST/DELETE
|
||||
`/api/projects/<id>/members` (single POST keying on optional `collection_id`);
|
||||
(4) `notify.notify_scope_role_granted` + `render_summary`; (5) viewer
|
||||
capability flags on the project/collection GETs. Frontend: (6) `ScopeMembersModal`,
|
||||
`CreateCollectionModal`, role-aware empty states in `CollectionDirectory` +
|
||||
`Catalog`. Tests: `test_s4_invitations_vertical.py`.
|
||||
|
||||
**Design decision (logged):** the C.2 scenarios write a membership row
|
||||
immediately and §15-notify an *existing* account, so S4 uses a **direct grant
|
||||
to an existing user** (looked up by email), NOT the per-RFC email-token-accept
|
||||
flow. Inviting a not-yet-account email is out of S4 scope (the
|
||||
admin-create-invite path already covers account provisioning).
|
||||
|
||||
## Pre-session state
|
||||
|
||||
- §22 three-tier refactor: S1 (mig 029, collection grain) @ v0.40.0; S2 (2nd
|
||||
collection) @ v0.41.0; S3 (scope-role enforcement, mig 030 `global` scope;
|
||||
collection-grain visibility) @ v0.42.0. `origin/main` = `e6bd69f`, clean.
|
||||
- The §B.2 four-layer resolver `auth.effective_scope_role(user, collection_id)`
|
||||
existed (S3); grants were applied administratively (DB / the Owner-authorized
|
||||
create surface) — no invite UI, no role-aware empty states.
|
||||
- The roadmap (design doc Part E) named S4 as the next slice. Memory pointer
|
||||
said `NEXT = S4`.
|
||||
|
||||
## Turn-by-turn arc
|
||||
|
||||
1. **Gate + claim.** Classified the `/goal s4` open as a coding session; ran
|
||||
`wgl-session-coding-init`. Peeked (no in-flight ohm sessions), claimed
|
||||
**0077**. Verified clean pushed `main` baseline.
|
||||
2. **Orient on S4.** Read design doc Part E (S4) + the C.2 / C.3 `@S4`
|
||||
scenarios. Confirmed shipped state from CHANGELOG/migrations (VERSION 0.42.0,
|
||||
mig 030 present).
|
||||
3. **Map the surfaces.** Two parallel `Explore` agents mapped the backend
|
||||
(memberships model, `effective_scope_role`, notify chokepoint `_emit_one`,
|
||||
the §6 `permission_state` floor, router structure) and the frontend
|
||||
(CollectionDirectory, Catalog, viewer/session shape, the per-RFC
|
||||
`InvitationsModal` template, API client conventions).
|
||||
4. **Key realization.** The C.2 scenarios dictate a **direct-grant** model
|
||||
(membership row written immediately + §15 notify to an existing account),
|
||||
distinct from the per-RFC token-accept flow. Logged the decision.
|
||||
5. **Backend (TDD).** Wrote `test_s4_invitations_vertical.py` first (the C.2 +
|
||||
C.3-flag spec), then implemented: `auth.can_invite_at_project` /
|
||||
`can_invite_at_collection`; new `memberships.py` (grant + broader-supersedes
|
||||
prune, revoke, list_for_project, user_by_email); new `api_memberships.py`
|
||||
(registered in `api.py`); `notify.notify_scope_role_granted` +
|
||||
`render_summary` case; viewer capability flags on the two `api_collections`
|
||||
GETs. 11 S4 tests green; **full suite 504 passed**. Committed (`c9fd1c5`).
|
||||
6. **Frontend.** `api.js` (getCollection, listScopeMembers, grantScopeMember,
|
||||
revokeScopeMember); `ScopeMembersModal` (Owner-only invite + members/revoke,
|
||||
scope picker bounded to reach); `CreateCollectionModal` (surfaces the S2
|
||||
endpoint, previously UI-less); `CollectionDirectory` rewritten for the
|
||||
role-aware empty states (C3.3/C3.4) + Owner "Members" control; `Catalog`
|
||||
propose-first empty state (C3.5) gated on `can_contribute`; small CSS.
|
||||
Build clean. Committed (`93cf506`).
|
||||
7. **Release.** Bumped VERSION + package.json → 0.43.0, CHANGELOG entry (minor,
|
||||
non-breaking), marked S4 shipped in the design doc. Re-ran the touched suites
|
||||
green. Committed (`ff54632`), pushed the branch.
|
||||
8. **PR + merge.** Created PR #21 via the Gitea API (`wgl-gitea-admin` helper),
|
||||
confirmed mergeable, merged (HTTP 200), FF-pulled local `main`, deleted the
|
||||
branch local + remote. Full suite on merged main: **504 passed**.
|
||||
9. **Memory + finalize.** Updated the project memory (S4 shipped, NEXT = S5)
|
||||
and the MEMORY.md index; finalized this transcript.
|
||||
|
||||
## Cut state (end of session)
|
||||
|
||||
| Repo | Branch | SHA | State |
|
||||
| --- | --- | --- | --- |
|
||||
| `ben.stull/rfc-app` | `main` | `2696e64` | clean, pushed; PR #21 merged |
|
||||
|
||||
- VERSION = **0.43.0** (mirrored in `frontend/package.json`).
|
||||
- New files: `backend/app/memberships.py`, `backend/app/api_memberships.py`,
|
||||
`backend/tests/test_s4_invitations_vertical.py`,
|
||||
`frontend/src/components/ScopeMembersModal.jsx`,
|
||||
`frontend/src/components/CreateCollectionModal.jsx`.
|
||||
- Touched: `backend/app/auth.py`, `notify.py`, `api.py`, `api_collections.py`;
|
||||
`frontend/src/api.js`, `App.css`, `components/CollectionDirectory.jsx`,
|
||||
`components/Catalog.jsx`; `CHANGELOG.md`, `VERSION`, the design doc.
|
||||
- No schema migration (mig high-water stays 030). Non-breaking — existing
|
||||
authz outcomes unchanged; the new UI is another way to write `memberships`
|
||||
rows that S3 wrote administratively.
|
||||
|
||||
### Deferred decisions
|
||||
|
||||
The `## Deferred decisions` section carried no entries — **no low-confidence
|
||||
calls this session.** The one real design fork (direct-grant vs.
|
||||
email-token-accept) was clearly resolved by the C.2 scenarios (they write the
|
||||
row immediately and §15-notify an existing account), so it was a decided call,
|
||||
documented in the Plan block, the CHANGELOG, and the design doc — not a
|
||||
low-confidence deferral.
|
||||
|
||||
## What lands on the operator's plate
|
||||
|
||||
- Nothing blocking. S4 is shipped, merged, and verified.
|
||||
- **Scope note to confirm at leisure:** inviting a *not-yet-account* email is
|
||||
out of S4 (the grant endpoint 404s an unknown email). The admin-create-invite
|
||||
path provisions accounts; composing the two (invite-by-email that provisions
|
||||
+ grants) is a candidate for S6's membership lifecycle, not a gap in S4.
|
||||
- **For the S6 SPEC merge:** S3's keystone reinterpretation of §B.1/§B.3 still
|
||||
needs to land in `SPEC.md` (carried forward, unchanged by S4).
|
||||
- Many stale local feature branches exist in the working copy (pre-existing,
|
||||
one checked out in another worktree) — untouched this session; housekeeping
|
||||
candidates whenever convenient.
|
||||
|
||||
## Prompt the operator can paste into the next session
|
||||
|
||||
```
|
||||
/goal s5 — in-app create-project + the global directory: the global-Owner
|
||||
create-project action (bot provisions a Gitea content repo + commits to
|
||||
projects.yaml), plus the deployment-directory role-aware empty states.
|
||||
Completes @S5 (C3.1–C3.2), per
|
||||
docs/design/2026-06-05-three-tier-projects-collections.md Part E (S5).
|
||||
```
|
||||
@@ -1,20 +0,0 @@
|
||||
# Session 0077.0 — Transcript
|
||||
|
||||
> App: ohm
|
||||
> Start: 2026-06-05T23-31 (PST)
|
||||
> Type: coding
|
||||
> Status: **PLACEHOLDER — claimed at session start; finalized at session end.**
|
||||
>
|
||||
> This file reserves session ID 0077 for ohm. The driver replaces this
|
||||
> body with the full transcript and renames the file to its final
|
||||
> SESSION-0077.0-TRANSCRIPT-2026-06-05T23-31--<end>.md form at session end.
|
||||
|
||||
## Launch prompt
|
||||
|
||||
_(launch prompt not captured at claim time)_
|
||||
|
||||
## 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._
|
||||
Reference in New Issue
Block a user