§13: make graduation's RFC number optional; add retire (soft delete)
Optional number (§13.2/§13.3): GraduateBody.rfc_id is now optional.
A blank/absent id graduates to `active` with `id: null`, leaving the
slug as the canonical identifier (§2.3). /graduate/check treats a blank
id as valid (ok:true); /graduate only validates the RFC-NNNN regex +
collision check when a number is supplied. The Graduate dialog allows an
empty number and renders number-less entries by slug (no RFC-undefined).
Retire (§3, §3.1, §13.7): new `retired` soft-delete state. An RFC's own
owners (frontmatter) and site `owner`-role holders — not app admins —
retire via POST /api/rfcs/<slug>/retire, an auto-merged meta-repo flip
PR (graduation machinery reused). Retired entries leave every browsing
surface: catalog, get_rfc (404 except site owner), discussion/branch
reads. Un-retire is site-owner-only (POST .../unretire), discoverable
via the owner-gated GET /api/admin/retired-rfcs ("Retired" admin tab).
Migration 025 widens the cached_rfcs.state CHECK to include 'retired'
(table rebuild, all columns preserved).
Tests: graduation no-number cases (active+null id by slug; check accepts
blank) added to test_graduation_vertical.py; new test_retire_vertical.py
covers perms (owner/site-owner allowed, admin/contributor 403),
catalog/read exclusion, and a graduate→retire→unretire round-trip. Full
backend suite 386 passing; frontend builds clean. SPEC §3/§3.1/§13
updated; CHANGELOG + VERSION → 0.33.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -530,6 +530,22 @@ export async function listBlockingPRs(slug) {
|
||||
return jsonOrThrow(await fetch(`/api/rfcs/${slug}/blocking-prs`))
|
||||
}
|
||||
|
||||
// §13.7 retire (soft delete). RFC owners + site owners may retire; only
|
||||
// site owners may un-retire. The backend gates both regardless of UI.
|
||||
export async function retireRFC(slug) {
|
||||
return jsonOrThrow(await fetch(`/api/rfcs/${slug}/retire`, { method: 'POST' }))
|
||||
}
|
||||
|
||||
export async function unretireRFC(slug) {
|
||||
return jsonOrThrow(await fetch(`/api/rfcs/${slug}/unretire`, { method: 'POST' }))
|
||||
}
|
||||
|
||||
// §13.7: the site-owner-only list of retired entries (for the admin
|
||||
// "Retired" surface, where un-retire lives).
|
||||
export async function listRetiredRFCs() {
|
||||
return jsonOrThrow(await fetch('/api/admin/retired-rfcs'))
|
||||
}
|
||||
|
||||
export async function graduateCheck(slug, { id }) {
|
||||
// Meta-only topology (§13.2): two fields — integer id + owners. No
|
||||
// repo name to validate.
|
||||
|
||||
Reference in New Issue
Block a user