49ba06e0c2
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>
62 lines
2.8 KiB
SQL
62 lines
2.8 KiB
SQL
-- §3 / §13.7: add the `retired` soft-delete state to cached_rfcs.
|
|
--
|
|
-- SQLite cannot ALTER a CHECK constraint in place, so we rebuild the table
|
|
-- with the expanded constraint and copy the rows across. cached_rfcs is a
|
|
-- §4 cache (reconstructible from Gitea by the reconciler), and nothing
|
|
-- holds a foreign key into it, so the rebuild is safe; we preserve the
|
|
-- existing rows anyway to avoid a needless full re-read on upgrade.
|
|
--
|
|
-- The rebuilt table must carry EVERY column cached_rfcs has accumulated,
|
|
-- including the ones added by later migrations via ALTER TABLE ADD COLUMN:
|
|
-- 009_per_rfc_models -> models_json
|
|
-- 010_funder -> funder_login
|
|
-- 021_proposed_use_case -> proposed_use_case
|
|
-- They are appended last (matching the live column order) and copied
|
|
-- across explicitly so nothing is dropped.
|
|
--
|
|
-- The migration runner wraps this file in a single BEGIN/COMMIT, so the
|
|
-- swap is atomic.
|
|
|
|
CREATE TABLE cached_rfcs_new (
|
|
slug TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
state TEXT NOT NULL CHECK (state IN ('super-draft', 'active', 'withdrawn', 'retired')),
|
|
rfc_id TEXT, -- 'RFC-NNNN' or NULL (NULL is also valid for an active RFC graduated without a number, §13.2)
|
|
repo TEXT, -- 'org/repo' or NULL; always NULL under the meta-only topology (§1)
|
|
proposed_by TEXT,
|
|
proposed_at TEXT,
|
|
graduated_at TEXT,
|
|
graduated_by TEXT,
|
|
owners_json TEXT NOT NULL DEFAULT '[]',
|
|
arbiters_json TEXT NOT NULL DEFAULT '[]',
|
|
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
body TEXT,
|
|
body_sha TEXT,
|
|
last_main_commit_at TEXT,
|
|
last_entry_commit_at TEXT,
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
models_json TEXT, -- 009_per_rfc_models
|
|
funder_login TEXT, -- 010_funder
|
|
proposed_use_case TEXT -- 021_proposed_use_case
|
|
);
|
|
|
|
INSERT INTO cached_rfcs_new
|
|
(slug, title, state, rfc_id, repo, proposed_by, proposed_at,
|
|
graduated_at, graduated_by, owners_json, arbiters_json, tags_json,
|
|
body, body_sha, last_main_commit_at, last_entry_commit_at, updated_at,
|
|
models_json, funder_login, proposed_use_case)
|
|
SELECT
|
|
slug, title, state, rfc_id, repo, proposed_by, proposed_at,
|
|
graduated_at, graduated_by, owners_json, arbiters_json, tags_json,
|
|
body, body_sha, last_main_commit_at, last_entry_commit_at, updated_at,
|
|
models_json, funder_login, proposed_use_case
|
|
FROM cached_rfcs;
|
|
|
|
DROP TABLE cached_rfcs;
|
|
ALTER TABLE cached_rfcs_new RENAME TO cached_rfcs;
|
|
|
|
CREATE INDEX idx_cached_rfcs_state ON cached_rfcs (state);
|
|
CREATE INDEX idx_cached_rfcs_last_active ON cached_rfcs (
|
|
COALESCE(last_main_commit_at, last_entry_commit_at) DESC
|
|
);
|