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
+5 -1
View File
@@ -650,11 +650,15 @@ def make_router(
# meta repo as pr_kind='meta_body_edit'; active RFC PRs live on
# the per-RFC repo as 'rfc_branch'. The API surface and the §10
# treatment are identical.
# Slice 5: §13.1 claim PRs (pr_kind='meta_claim') are also
# exposed through this surface — the merge path is the only
# affordance an admin needs, and the §10 review machinery
# gracefully degrades for frontmatter-only PRs.
row = db.conn().execute(
"""
SELECT * FROM cached_prs
WHERE rfc_slug = ? AND pr_number = ?
AND pr_kind IN ('rfc_branch', 'meta_body_edit')
AND pr_kind IN ('rfc_branch', 'meta_body_edit', 'meta_claim')
""",
(slug, pr_number),
).fetchone()