#!/usr/bin/env python3 from __future__ import annotations import argparse import json from collections import defaultdict from pathlib import Path from typing import Any import yaml ROOT = Path(__file__).resolve().parents[1] def _status_value(row: dict[str, Any]) -> str: return str((row or {}).get("status") or "active").strip().lower() def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--registry", default="spec/13_formula_registry.yaml") ap.add_argument("--lifecycle", default="spec/51_formula_lifecycle_registry.yaml") args = ap.parse_args() # 1. Read spec/13_formula_registry.yaml path = ROOT / args.registry data = yaml.safe_load(path.read_text(encoding="utf-8")) formulas = ((data or {}).get("formula_registry") or {}).get("formulas") or {} families: dict[str, list[str]] = defaultdict(list) for fid in formulas.keys(): family = fid.rsplit("_V", 1)[0] families[family].append(fid) # 2. Read spec/51_formula_lifecycle_registry.yaml lifecycle_path = ROOT / args.lifecycle if not lifecycle_path.exists(): print(f"Lifecycle registry not found: {lifecycle_path}") return 1 lifecycle_data = yaml.safe_load(lifecycle_path.read_text(encoding="utf-8")) lifecycle_formulas = lifecycle_data.get("formulas", []) lifecycle_map = {f.get("formula_id"): f.get("lifecycle_state") for f in lifecycle_formulas if f.get("formula_id")} # 3. Validation logic missing_ids = [] invalid_state_ids = [] valid_states = {"ACTIVE", "DEPRECATED", "DATA_GATED", "PENDING"} for fid in formulas.keys(): state = lifecycle_map.get(fid) if not state: missing_ids.append(fid) elif state not in valid_states: invalid_state_ids.append((fid, state)) missing_status_count = len(missing_ids) + len(invalid_state_ids) gate = "PASS" if missing_status_count == 0 else "FAIL" if missing_ids: print(f"Missing formula registrations in 51_formula_lifecycle_registry.yaml: {missing_ids}") if invalid_state_ids: print(f"Invalid lifecycle states detected: {invalid_state_ids}") result = { "formula_id": "FORMULA_VERSION_LIFECYCLE_V1", "formula_count": len(formulas), "missing_status_count": missing_status_count, "inferred_active_count": 0, "family_count": len(families), "active_count": sum(1 for state in lifecycle_map.values() if state == "ACTIVE"), "gate": gate, } out = ROOT / "Temp" / "formula_version_lifecycle_v1.json" out.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8") print(json.dumps(result, ensure_ascii=False, indent=2)) return 0 if gate == "PASS" else 1 if __name__ == "__main__": raise SystemExit(main())