Post-v1: per-RFC credential delegation (funder role) folded into §6.7

Second §19.2 settlement after v1. New §6.7 alongside §6.6: optional
`funder:` frontmatter field names a single gitea_login; a
`funder_consents` app-db row records funder-side opt-in; both halves
required for the binding to activate (two-key rule). Funder universe
replaces — does not augment — the operator universe per-RFC for
attribution-clean resolution. Funder role grants zero §6.1/§6.3
authority. Three revocation paths each restore the operator-credentials
status quo.

§19.2's credential-delegation entry is split: lighter half marked
settled with a pointer to §6.7; operational-realities half (mid-call
failure, rotation, billing, rate-limit attribution) lives on as its
own entry. Test suite is 125/125 green (106 prior + 19 new).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-05-25 06:08:43 -07:00
parent a255429e57
commit 55a8be051a
12 changed files with 1437 additions and 43 deletions
+25 -14
View File
@@ -1,4 +1,5 @@
"""§6.6 per-RFC model availability — the resolver.
"""§6.6 per-RFC model availability — the resolver, extended in §6.7
with the funder-universe layer.
The meta-repo entry's optional `models:` frontmatter and the operator's
provisioned providers (the §18 `ENABLED_MODELS` universe) combine into a
@@ -8,16 +9,22 @@ invocation, the §9.1 tag suggestions when graduation is in scope.
Rule, in one place:
- The base universe is normally the operator's provisioned providers.
When a consenting funder is in effect for the RFC per §6.7, the base
universe is replaced (not augmented) by the funder's registered
universe — the subset of operator-enabled picker keys the funder has
supplied credentials for.
- Cache row's `models_json` is NULL → the field is absent on the entry.
Resolved list = the operator's provisioned universe.
Resolved list = the base universe.
- Cache row's `models_json` is a JSON array → the field is set on the
entry. Resolved list = intersection of the array with the operator's
provisioned universe, preserving the entry's stated order.
entry. Resolved list = intersection of the array with the base
universe, preserving the entry's stated order.
The empty case folds in naturally: an entry with `models: []` yields an
empty intersection, and an entry whose listed models are no longer
provisioned by the operator also yields an empty intersection. Callers
treat both the same — surfaces refuse cleanly per §6.6.
The empty case folds in naturally: `models: []` yields empty, an entry
whose listed models aren't in the operator's enabled set yields empty,
a consenting funder whose registrations don't intersect the operator's
enabled set yields empty. Callers treat all four the same — refuse
cleanly per §6.6.
The function is slug-aware and provider-aware; it does not depend on
the FastAPI request lifecycle, which keeps it cheap to call inside any
@@ -27,32 +34,36 @@ from __future__ import annotations
import json
from . import db
from . import db, funder
from .providers import BaseProvider
def resolve_models_for_rfc(
slug: str, providers: dict[str, BaseProvider]
) -> list[str]:
"""Return the per-RFC resolved model keys per §6.6.
"""Return the per-RFC resolved model keys per §6.6, extended by §6.7.
The first entry is the RFC's default model. An empty list means
AI is unavailable on this RFC and callers refuse the AI surface.
"""
universe = list(providers.keys())
# §6.7: the funder universe (if any) replaces the operator universe
# as the base set the §6.6 frontmatter intersects against.
funder_universe = funder.resolve_funder_universe(slug, providers)
base_universe = funder_universe if funder_universe is not None else list(providers.keys())
row = db.conn().execute(
"SELECT models_json FROM cached_rfcs WHERE slug = ?",
(slug,),
).fetchone()
if row is None or row["models_json"] is None:
return universe
return list(base_universe)
try:
listed = [str(m) for m in json.loads(row["models_json"])]
except (json.JSONDecodeError, TypeError):
return universe
return list(base_universe)
if not listed:
return []
return [m for m in listed if m in providers]
base_set = set(base_universe)
return [m for m in listed if m in base_set]
def default_model_for_rfc(