Slice 8 WIP: §12 hygiene + §10.7 + routing + rollback cleanup
- Add §12 30/90 hygiene scheduler in hygiene.py, mirroring the
DigestScheduler shape; wires next to digest in main.py with the
same start/stop/run_tick test seam.
- Extend bot.delete_branch to accept actor=None for system gestures,
per §15.9 (actor_user_id=NULL, on_behalf_of=bot_login).
- Convert every branches/{branch} route in api_branches.py and
api_prs.py to {branch:path}; move the bare GET to the bottom of
the router so deeper GETs match before greedy-path swallow.
- Extend api_prs.py's _require_pr to accept pr_kind='meta_metadata'
so the §9.5 metadata-pane PRs land an in-app merge.
- Graduation rollback now deletes the graduate-<slug>-<6hex> branch
after closing the PR — §19.2 candidate that lands here.
- Email-bounce webhook gains a WEBHOOK_EMAIL_BOUNCE_SECRET seam.
- FakeGitea grows a DELETE /branches/{branch:path} handler and a
slashed-branch read; integration tests for the hygiene vertical
cover the 30d close, 90d delete, post-merge delete, pinned
exemption, per-user cursor preservation, no-notification rule,
and the graduation-rollback cleanup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -77,7 +77,7 @@ def make_router(
|
||||
# produces the open-pr call; the draft is just a starting point.
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
@router.post("/api/rfcs/{slug}/branches/{branch}/pr-draft")
|
||||
@router.post("/api/rfcs/{slug}/branches/{branch:path}/pr-draft")
|
||||
async def draft_pr_text(slug: str, branch: str, request: Request) -> dict[str, Any]:
|
||||
viewer = auth.require_contributor(request)
|
||||
rfc = _require_active_rfc(slug)
|
||||
@@ -107,7 +107,7 @@ def make_router(
|
||||
# branch's read_public unconditionally.
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
@router.post("/api/rfcs/{slug}/branches/{branch}/open-pr")
|
||||
@router.post("/api/rfcs/{slug}/branches/{branch:path}/open-pr")
|
||||
async def open_pr(slug: str, branch: str, body: OpenPRBody, request: Request) -> dict[str, Any]:
|
||||
viewer = auth.require_contributor(request)
|
||||
rfc = _require_active_rfc(slug)
|
||||
@@ -660,11 +660,16 @@ def make_router(
|
||||
# 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.
|
||||
# Slice 8: §9.5 metadata-pane PRs (`meta_metadata`) land here
|
||||
# too per the §19.2 "in-app merge for metadata PRs" candidate.
|
||||
# The diff-rendered review surface degrades gracefully — there
|
||||
# is no body diff worth reviewing — but the merge gesture lands
|
||||
# in-app rather than forcing the Gitea round-trip.
|
||||
row = db.conn().execute(
|
||||
"""
|
||||
SELECT * FROM cached_prs
|
||||
WHERE rfc_slug = ? AND pr_number = ?
|
||||
AND pr_kind IN ('rfc_branch', 'meta_body_edit', 'meta_claim')
|
||||
AND pr_kind IN ('rfc_branch', 'meta_body_edit', 'meta_claim', 'meta_metadata')
|
||||
""",
|
||||
(slug, pr_number),
|
||||
).fetchone()
|
||||
|
||||
Reference in New Issue
Block a user