v0.31.0: meta-only repository topology (ROADMAP #36)
Retire the per-RFC-repo model. RFCs now live in their meta-repo entry (rfcs/<slug>.md) for their whole life; graduation is an in-place super-draft → active state flip that keeps the body in the entry — no repo creation, no body-strip, no five-step transaction, no rollback. SPEC: §1 topology rewritten (one meta/content repository, no per-RFC repos) with a deployer-facing "single content repository" framing; §2 (repo: always-null), §3 (active is in-place), §4, §9.8 (handoff frictions dissolve), and §13 fully rewritten (two-field dialog, "The flip", §13.6 RFC-0001 fold-back record). Code: graduation collapses to open+merge one frontmatter PR; branch/PR/ chat dispatch re-keyed on meta-residency (repo IS NULL) so active RFCs edit on the meta repo exactly as super-drafts do; the two "RFC has no repo" 409 guards removed; promote-to-branch slug-embeds an active RFC's auto-branch (edit-<slug>-<hex>) for shared-repo cache attribution; refresh_meta_branches + hygiene branch-resolution include meta-resident actives; the §9.8 read-only guard + pre_graduation_history scoped to legacy per-repo only; dead bot primitives + GraduateDialog repo field + blocking-PR popover removed. The repo: frontmatter field and the /blocking-prs endpoint are retained (schema stability / informational). Tests: graduation suite rewritten to the flip model; e2e + hygiene updated. Full backend suite 375 passed; frontend builds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -119,19 +119,20 @@ def test_full_user_lifecycle_propose_through_hygiene(app_with_fake_gitea):
|
||||
r = client.post(f"/api/rfcs/ohm/prs/{body_pr}/merge")
|
||||
assert r.status_code == 200, r.text
|
||||
|
||||
# --- 7. Graduate the super-draft. ---
|
||||
# --- 7. Graduate the super-draft (in-place flip, §13). ---
|
||||
r = client.post(
|
||||
"/api/rfcs/ohm/graduate?_sync=1",
|
||||
json={"rfc_id": "RFC-0001", "repo_name": "rfc-0001-ohm",
|
||||
"owners": ["ben"]},
|
||||
json={"rfc_id": "RFC-0001", "owners": ["ben"]},
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json()["succeeded"] is True
|
||||
d = client.get("/api/rfcs/ohm").json()
|
||||
assert d["state"] == "active"
|
||||
assert d["repo"] == "wiggleverse/rfc-0001-ohm"
|
||||
# Meta-only topology (§1): no per-RFC repo — the active RFC lives
|
||||
# in its meta entry, `repo` stays null.
|
||||
assert d["repo"] is None
|
||||
|
||||
# --- 8. Alice opens a PR on the now-active RFC's per-RFC repo. ---
|
||||
# --- 8. Alice opens a PR on the now-active RFC (meta repo). ---
|
||||
# v0.16.0 (item #12): ben is the RFC owner now; alice needs a
|
||||
# per-RFC contributor invitation to cut a branch. In the
|
||||
# production flow, ben would invite her via /invitations and
|
||||
@@ -200,8 +201,8 @@ def test_full_user_lifecycle_propose_through_hygiene(app_with_fake_gitea):
|
||||
)
|
||||
assert counters["deleted_post_merge"] >= 1, counters
|
||||
|
||||
# The branch is gone from FakeGitea + cached row flipped.
|
||||
assert active_branch not in fake.branches[("wiggleverse", "rfc-0001-ohm")]
|
||||
# The branch is gone from FakeGitea (meta repo) + cached row flipped.
|
||||
assert active_branch not in fake.branches[("wiggleverse", "meta")]
|
||||
cached = db.conn().execute(
|
||||
"SELECT state FROM cached_branches WHERE rfc_slug = 'ohm' AND branch_name = ?",
|
||||
(active_branch,),
|
||||
|
||||
Reference in New Issue
Block a user