Post-v1: per-RFC model availability (UX half) folded into §6.6

First §19.2 candidate settled after v1. The heavier per-RFC-model
topic subdivided into UX (this) and credential delegation + funder
role (still §19.2). New §6.6 carries the rule: an optional `models:`
frontmatter field on the meta-repo RFC entry; absent inherits the
operator universe, populated narrows the picker to the intersection
with provisioned providers, `[]` opts the RFC out of AI entirely.
The first resolved entry is the RFC default. §18's ENABLED_MODELS is
reframed as the operator universe.

Code: migration 009 adds nullable cached_rfcs.models_json (NULL ≠ []
is load-bearing); entry.py grows the optional field with absent-vs-
empty round-tripping in parse/serialize; new models_resolver module
holds the rule; api_branches replaces /api/models with the slug-aware
/api/rfcs/{slug}/models and threads the chat + reask paths through
the resolver; api_prs §10.2 uses the resolver and extends the stub
fallback to the opt-out case; frontend passes slug to listModels.

Tests 106/106 green (96 prior + 10 in test_per_rfc_models.py). No
behavioral change for entries without `models:` — operator universe
preserved as default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-05-25 05:42:15 -07:00
parent 36635049c7
commit a255429e57
11 changed files with 755 additions and 57 deletions
+7 -2
View File
@@ -76,13 +76,16 @@ async def refresh_meta_repo(config: Config, gitea: Gitea) -> None:
def _upsert_cached_rfc(entry: entry_mod.Entry, body_sha: str) -> None:
# §6.6: models_json stays NULL when the frontmatter key is absent
# (inherit operator universe) and '[]' for the explicit opt-out.
models_json = json.dumps(entry.models) if entry.models is not None else None
db.conn().execute(
"""
INSERT INTO cached_rfcs
(slug, title, state, rfc_id, repo, proposed_by, proposed_at,
graduated_at, graduated_by, owners_json, arbiters_json, tags_json,
body, body_sha, last_entry_commit_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
models_json, body, body_sha, last_entry_commit_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
ON CONFLICT(slug) DO UPDATE SET
title = excluded.title,
state = excluded.state,
@@ -95,6 +98,7 @@ def _upsert_cached_rfc(entry: entry_mod.Entry, body_sha: str) -> None:
owners_json = excluded.owners_json,
arbiters_json = excluded.arbiters_json,
tags_json = excluded.tags_json,
models_json = excluded.models_json,
body = excluded.body,
body_sha = excluded.body_sha,
last_entry_commit_at = datetime('now'),
@@ -113,6 +117,7 @@ def _upsert_cached_rfc(entry: entry_mod.Entry, body_sha: str) -> None:
json.dumps(entry.owners),
json.dumps(entry.arbiters),
json.dumps(entry.tags),
models_json,
entry.body,
body_sha,
),