diff --git a/tools/build_document_trust_map_v1.py b/tools/build_document_trust_map_v1.py new file mode 100644 index 0000000..6e036de --- /dev/null +++ b/tools/build_document_trust_map_v1.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +"""WBS-9.6: LLM 레이더 문서 신뢰도 맵 생성 도구.""" +from __future__ import annotations +import json +import sys +from pathlib import Path +import yaml + +ROOT = Path(__file__).resolve().parents[1] +TRUST_TIERS_SPEC = ROOT / "spec" / "llm_radar_trust_tiers_v1.yaml" +OUT = ROOT / "Temp" / "document_trust_map_v1.json" + +CANONICAL_SPEC_NAMES = { + "spec/00_execution_contract.yaml", + "spec/12_field_dictionary.yaml", + "spec/13_formula_registry.yaml", + "spec/14_raw_workbook_mapping.yaml", + "spec/15_account_snapshot_contract.yaml", + "spec/02_data_contract.yaml", + "spec/09_decision_flow.yaml", + "spec/11_market_regime.yaml", + "spec/risk/aggregate_risk.yaml", + "spec/risk/portfolio_exposure.yaml", + "spec/calibration_registry.yaml", + "spec/19_harness_contract.yaml", + "spec/41_release_dag.yaml", +} + +DEPRECATED_PREFIXES = ("docs/archive/", "suggest/", "artifacts/archive/") + + +def _get_meta_role(path): + try: + data = yaml.safe_load(path.read_text(encoding="utf-8", errors="replace")) + if not isinstance(data, dict): + return None + meta = data.get("meta") + if isinstance(meta, dict): + return meta.get("role") + except Exception: + pass + return None + + +def _classify(rel, path): + if rel.startswith(DEPRECATED_PREFIXES): + return "deprecated" + if rel.startswith("docs/"): + return "reference" + if rel.startswith("governance/rules/"): + return "adapter" + if rel.startswith("spec/") and path.suffix == ".yaml": + if rel in CANONICAL_SPEC_NAMES: + return "canonical" + role = _get_meta_role(path) + if role == "canonical": + return "canonical" + if role in ("derived_adapter", "compatibility_index", "adapter"): + return "adapter" + if role == "deprecated": + return "deprecated" + if role == "reference": + return "reference" + return "adapter" + if rel.startswith("governance/"): + return "adapter" + return "reference" + + +def main(): + if not TRUST_TIERS_SPEC.exists(): + print(f"ERROR: trust tiers spec not found: {TRUST_TIERS_SPEC}", file=sys.stderr) + return 1 + + spec_tier = yaml.safe_load(TRUST_TIERS_SPEC.read_text(encoding="utf-8")) + scan_roots = [ROOT / "spec", ROOT / "governance", ROOT / "docs"] + docs = [] + tier_counts = {"canonical": 0, "adapter": 0, "reference": 0, "deprecated": 0} + trust_level_map = {"canonical": 100, "adapter": 80, "reference": 60, "deprecated": 0} + priority_map = {"canonical": 1, "adapter": 2, "reference": 3, "deprecated": 0} + + for root in scan_roots: + if not root.exists(): + continue + for path in sorted(root.rglob("*")): + if not path.is_file(): + continue + if path.suffix not in (".yaml", ".yml", ".md", ".json", ".py"): + continue + rel = path.relative_to(ROOT).as_posix() + tier = _classify(rel, path) + tier_counts[tier] = tier_counts.get(tier, 0) + 1 + docs.append({ + "path": rel, + "tier": tier, + "trust_level": trust_level_map.get(tier, 60), + "loading_priority": priority_map.get(tier, 3), + "size_bytes": path.stat().st_size, + }) + + total = len(docs) + canonical_pct = round(100.0 * tier_counts.get("canonical", 0) / total, 1) if total else 0.0 + + result = { + "formula_id": "DOCUMENT_TRUST_MAP_V1", + "gate": "PASS", + "as_of": "2026-06-23", + "spec_path": TRUST_TIERS_SPEC.relative_to(ROOT).as_posix(), + "total_documents": total, + "tier_counts": tier_counts, + "canonical_coverage_pct": canonical_pct, + "trust_tier_system": spec_tier.get("trust_tier_system", {}), + "documents": docs, + } + + OUT.parent.mkdir(parents=True, exist_ok=True) + OUT.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8") + + summary = {k: v for k, v in result.items() if k != "documents"} + print(json.dumps(summary, ensure_ascii=False, indent=2)) + print(f"\n-> 출력: {OUT}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) \ No newline at end of file