"""Phase Z catalog invariant test — real `frame_contracts.yaml` 1:1 mapping verify. IMP-05 L4 lock per Claude #13 §3 : - real catalog read (purpose 자체 = real catalog 검증) - template_id ↔ frame_id 1:1 mapping (Codex #6 terminology — 2 reference keys for same entry) - fail fast with explicit message if catalog policy changes Codex #5 verified : 11 templates / 11 frames, all unique = 1:1 mapping confirm (2026-05-13). Codex #7 generalization guardrail : real catalog OK (purpose 자체) — NOT sample-hardcoding. """ from __future__ import annotations from pathlib import Path import pytest import yaml PROJECT_ROOT = Path(__file__).parent.parent CATALOG_PATH = PROJECT_ROOT / "templates" / "phase_z2" / "catalog" / "frame_contracts.yaml" def _load_catalog() -> dict: with CATALOG_PATH.open(encoding="utf-8") as f: return yaml.safe_load(f) def test_catalog_template_id_to_frame_id_one_to_one(): """Verify each catalog entry has unique template_id + unique frame_id (1:1 reference keys). Fails fast if the catalog policy ever drifts from this assumption — IMP-05 dedup relies on `template_id` as the runtime key and assumes one frame per template. """ catalog = _load_catalog() template_ids = [] frame_ids = [] for entry_key, entry in catalog.items(): if not isinstance(entry, dict): continue tid = entry.get("template_id") fid = entry.get("frame_id") assert tid is not None, f"entry {entry_key} missing template_id" assert fid is not None, f"entry {entry_key} missing frame_id" template_ids.append(tid) frame_ids.append(str(fid)) duplicate_templates = [t for t in template_ids if template_ids.count(t) > 1] duplicate_frames = [f for f in frame_ids if frame_ids.count(f) > 1] assert not duplicate_templates, ( "Phase Z catalog currently expects one template_id per frame_id; " "update dedup policy if this changes. " f"Duplicate template_ids found: {set(duplicate_templates)}" ) assert not duplicate_frames, ( "Phase Z catalog currently expects one template_id per frame_id; " "update dedup policy if this changes. " f"Duplicate frame_ids found: {set(duplicate_frames)}" ) assert len(template_ids) == len(frame_ids), ( "Phase Z catalog template_id count must equal frame_id count " f"(templates={len(template_ids)}, frames={len(frame_ids)})." ) def test_catalog_entry_count_matches_frame_count(): """Sanity guard — each entry contributes one template_id + one frame_id.""" catalog = _load_catalog() entry_count = sum(1 for v in catalog.values() if isinstance(v, dict)) template_count = sum( 1 for v in catalog.values() if isinstance(v, dict) and v.get("template_id") is not None ) frame_count = sum( 1 for v in catalog.values() if isinstance(v, dict) and v.get("frame_id") is not None ) assert entry_count == template_count == frame_count, ( f"catalog shape inconsistent: entries={entry_count} " f"templates={template_count} frames={frame_count}" )