Slice 5: graduation per §13

The §13.3 transactional sequence flips a super-draft to active —
five steps with paired undoes, an in-process orchestrator fed by
an asyncio.Queue, the §17 SSE endpoint streaming step transitions
to the dialog. Each step is a new bot primitive that logs an
`actions` row, bracketed by `graduate_start` / `graduate_complete`
for the linkable audit sequence. Rollback runs the undoes in
reverse from the last completed step; merge_pr has no undo by
design per §13.5.

The §9.8 precondition gate is enforced server-side at the top of
POST /graduate so the §13.3 rollback complexity does not grow.
The §13.4 chat migration is a database semantic no-op — the
(slug, branch_name='main') threads keep their identity, only the
interpretation changes. The §9.8 pre-graduation history surfaces
via a new _is_meta_target(rfc, branch) dispatch helper and lands
as pre_graduation_history on /main.

§13.1 claim flow landed alongside since it's the prerequisite for
non-admin graduation — bot.open_claim_pr plus broadening
api_prs._require_pr to accept meta_claim.

45/45 tests green; ten new integration tests cover the validator,
the §9.8 precondition refusal, happy path with audit verification,
mid-sequence rollback at steps 2 and 3, concurrent refusal,
chat-survives-without-data-movement, pre-graduation history, and
the §13.1 claim PR cycle.

SPEC.md §19.1 rewritten for Slice 6 (notifications); §19.2 grew
four candidates surfaced during the slice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-05-24 21:52:29 -07:00
parent 4565a6cb95
commit 1b0968a9a2
14 changed files with 2872 additions and 172 deletions
+42 -20
View File
@@ -148,22 +148,42 @@ Open `http://localhost:5173`. Sign in with your owner-zero Gitea
account. The catalog should appear empty; the **+ Propose New RFC**
button at the bottom opens the propose modal.
## What slice 1 lets you do
## What the build lets you do so far
End-to-end: propose a new RFC → an idea PR opens against the meta
repo → an owner merges from the pending-idea view → the super-draft
appears in the catalog → opening it renders the body.
Slices 15 are shipped. End-to-end paths the app supports today:
This exercises the §4 cache (webhook + reconciler), the §6 permission
model (the owner-only merge button, the contributor-only propose
modal), the §1 bot wrapper (every Git write goes through it, every
commit and PR carries the `On-behalf-of:` trailer), and the §9
propose-merge-render path.
- **Propose → idea PR → merge → super-draft** (Slice 1, §9.1–§9.3).
- **Super-draft body editing** via meta-repo edit branches, with AI
participation, the change-card panel, manual flushes, threads,
flags, and DiffView (Slice 4, §9.5–§9.7 + §8 inherited).
- **The §8 active-RFC view** in full: per-branch chat, AI
participation through the `<change>` protocol, accept / decline /
edit, manual-edit flushes, sub-threads, flags, DiffView (Slice 2,
§8 in full).
- **The §10 PR flow** against both per-RFC repos and meta-repo edit
branches: open, AI-drafted title and description, the §10.3
review page with the per-user seen-cursor, review threads,
merge, withdraw, the §10.9 conflict-replay path (Slice 3 + Slice 4's
routing-collapse extension, §10 in full).
- **§13 graduation** with the three-field dialog, the precondition
popover for blocking body-edit PRs, the SSE-streamed five-step
sequence, rollback on mid-sequence failure, and the §9.8
pre-graduation history affordance on the new RFC view (Slice 5,
§13 in full).
- **§13.1 ownership claim** as a meta-repo PR adding the claimant
to the entry's `owners:` field; admin/owner merges the PR (Slice 5).
Out of scope for slice 1: the active-RFC view (§8), per-branch chat,
AI participation, the change-card panel, PRs against per-RFC repos,
graduation, notifications, the landing page's full polish. Those
slices are listed in [`docs/DEV.md`](./docs/DEV.md).
This exercises the §4 cache (webhook + reconciler), the §6
permission model in full, the §1 bot wrapper (every Git write goes
through it, every commit and PR carries the `On-behalf-of:`
trailer), and the §17 routing-collapse rule that lets active and
super-draft surfaces share their endpoints.
Out of scope for the slices shipped so far: notifications (Slice 6,
§15), landing-page and `/philosophy` chrome polish (Slice 7, §14),
the §12 30/90 branch-hygiene timers (Slice 8). The full slicing
plan and the next slice's brief live in
[`docs/DEV.md`](./docs/DEV.md).
## Verifying it worked
@@ -177,11 +197,12 @@ After bring-up:
## Seeding an active RFC for §8 testing
Slice 2 (the active-RFC view per §8) needs an entry whose `state` is
`active` and whose per-RFC repo exists. Slice 5's graduation flow
will land the proper path; until then, `scripts/seed_test_rfc.py` is
the dev shortcut. Sign in once via OAuth so a `users` row exists,
then:
With Slice 5 shipped, the `/graduate` flow in the app is the
canonical path from super-draft to active. The
[`scripts/seed_test_rfc.py`](./scripts/seed_test_rfc.py) shortcut is
still around for dev sessions that want an active RFC without
running the §9.1 propose flow and the §13 graduation dialog by
hand. Sign in once via OAuth so a `users` row exists, then:
```sh
cd backend && .venv/bin/python ../scripts/seed_test_rfc.py \
@@ -190,8 +211,9 @@ cd backend && .venv/bin/python ../scripts/seed_test_rfc.py \
```
The script creates `wiggleverse/rfc-NNNN-<slug>`, seeds `RFC.md` on
`main`, registers the webhook, and graduates the meta entry. The §8
surface at `/rfc/<slug>` then has something real to render.
`main`, registers the webhook, and graduates the meta entry as a
bootstrap-only direct write. The §8 surface at `/rfc/<slug>` then
has something real to render.
## Troubleshooting