v0.48.0 — SLICE-2: collection field schema + central validation (§22.4a)
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>
This commit is contained in:
@@ -45,6 +45,21 @@ def _enabled_models_from_config(config_json: str | None) -> list[str] | None:
|
||||
return [str(m) for m in em] if isinstance(em, list) else None
|
||||
|
||||
|
||||
def _fields_from_config(config_json: str | None) -> dict | None:
|
||||
"""§22.4a SLICE-2 per-collection metadata field schema from a `config_json`
|
||||
blob, or None when the collection declares no `fields:`. The stored value is
|
||||
already normalized by `metadata_schema.parse_fields` at ingest, so it's
|
||||
served verbatim."""
|
||||
if not config_json:
|
||||
return None
|
||||
try:
|
||||
cfg = json.loads(config_json)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
return None
|
||||
fields = cfg.get("fields") if isinstance(cfg, dict) else None
|
||||
return fields if isinstance(fields, dict) and fields else None
|
||||
|
||||
|
||||
def default_collection_id(project_id: str) -> str:
|
||||
"""The id of a project's default (S1: sole) collection. Falls back to the
|
||||
literal 'default' when the project has no collection row yet."""
|
||||
@@ -102,7 +117,12 @@ def get_collection(collection_id: str) -> dict | None:
|
||||
if row is None:
|
||||
return None
|
||||
out = dict(row)
|
||||
out["enabled_models"] = _enabled_models_from_config(out.pop("config_json", None))
|
||||
config_json = out.pop("config_json", None)
|
||||
out["enabled_models"] = _enabled_models_from_config(config_json)
|
||||
# §22.4a SLICE-2: serve the collection's metadata field schema (None when
|
||||
# the collection declares no `fields:` — INV-5, the default `document`
|
||||
# collection is unaffected).
|
||||
out["fields"] = _fields_from_config(config_json)
|
||||
out["entry_noun"] = entry_noun(out["type"])
|
||||
return out
|
||||
|
||||
|
||||
Reference in New Issue
Block a user