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:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user