Collections declare a `fields:` schema in `.collection.yaml`; entries carry
typed metadata (enum/tags/text). New central `metadata_schema` module parses
the schema leniently (INV-3) and validates entry values — advisory at read
(corpus mirror flags violations as `metadata_malformed` without hard-failing),
the enforcement point for the write boundary (edit endpoints land SLICE-4/5).
- app/metadata_schema.py: parse_fields (lenient/normalizing) + validate
- registry.parse_collection_manifest reads `fields:` into collection config
- collections.get_collection unpacks `fields`; served by the collection API
- cache._refresh_collection_corpus validates each entry advisory-only
Non-breaking, opt-in: no `fields:` → unchanged (INV-5); default `document`
collection declares none (N=1 unchanged). No DB migration — schema rides in
collections.config_json. SLICE-2 of
docs/design/2026-06-06-configurable-collection-metadata.md §7.2.
Backend suite green (601 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The displayed noun for an entry is a framework concept keyed on the collection's
immutable type — document→"RFC", specification→"Spec", bdd→"Feature" — not
deployment content. The chrome reads it from the API instead of hardcoding "RFC".
- collections.ENTRY_NOUN + entry_noun(type) (unknown type → generic "RFC").
- Surfaced as entry_noun on get_collection, list_collections items, the
/api/deployment directory items, and GET /api/projects/:id.
- Frontend: Catalog reads entry_noun for the "+ Propose New <noun>" control;
ProposeModal fetches the active collection's noun for its title + field copy.
- test_s6_entry_noun_vertical.py: map + API surfacing (collection + directory).
Scope note: this is §22.4a item (2) — terminology. The deeper type-module work
(item 1 per-type frontmatter schemas; item 3 specification release-planning +
bdd scenario/coverage surfaces) is flagged OPEN in the design doc and is handed
off to a follow-up spec+slice.
Backend 528 passed; frontend 30 passed + build green. Part of v0.45.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A collection's .collection.yaml may carry an enabled_models list that NARROWS
its project's universe, which narrows the deployment ENABLED_MODELS. Resolution
(extending §6.6/§6.7): funder ∩ per-entry models ∩ collection ∩ project, with
the operator providers as the ceiling.
- migration 031: additive collections.config_json (parallels projects.config_json).
- registry.parse_collection_manifest: read enabled_models into CollectionEntry.config
(absent = inherit, present incl. [] = narrow; [] opts the collection out of AI);
reject a non-list. _upsert_named_collection persists config_json. The default
collection (from projects.yaml) leaves it NULL — it inherits the project.
- models_resolver: _scope_narrowed_universe narrows the operator universe by the
entry's project then collection enabled_models; the funder universe is bounded
by it too. A collection cannot widen its project (narrowing from the ceiling).
- collections.get_collection surfaces enabled_models; GET
/api/projects/:id/collections/:cid returns it.
- test_s6_collection_models_vertical.py: 9 cases (parse, the narrowing chain
incl. opt-out + cannot-widen, API surfacing).
Backend: 525 passed. Per SPEC §22.12. Releases as part of v0.45.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>