#!/usr/bin/env python3 from __future__ import annotations import json import sys from pathlib import Path from typing import Any import yaml ROOT = Path(__file__).resolve().parents[1] PLAN_PATH = ROOT / "governance" / "todo" / "v8_9_p3_adoption_plan.yaml" DECISION_FLOW_PATH = ROOT / "spec" / "09_decision_flow.yaml" MANIFEST_PATH = ROOT / "runtime" / "active_artifact_manifest.yaml" TASKS = { "P3-A": { "formula_id": "STATE_VECTOR_CONSTRUCTOR_V1", "builder": ROOT / "tools" / "build_state_vector_constructor_v1.py", "spec_path": ROOT / "spec" / "formulas" / "domains" / "portfolio.yaml", "schema_path": ROOT / "schemas" / "generated" / "state_vector_constructor_v1.schema.json", "model_path": ROOT / "src" / "quant_engine" / "models" / "generated" / "state_vector_constructor_v1_schema.py", "temp_path": ROOT / "Temp" / "state_vector_constructor_v1.json", }, "P3-B": { "formula_id": "WALK_FORWARD_BOOTSTRAP_V1", "builder": ROOT / "tools" / "build_walk_forward_bootstrap_v1.py", "spec_path": ROOT / "spec" / "formulas" / "domains" / "simulation.yaml", "schema_path": ROOT / "schemas" / "generated" / "walk_forward_bootstrap_v1.schema.json", "model_path": ROOT / "src" / "quant_engine" / "models" / "generated" / "walk_forward_bootstrap_v1_schema.py", "temp_path": ROOT / "Temp" / "walk_forward_bootstrap_v1.json", }, "P3-C": { "formula_id": "TRANSITION_SET_ENUMERATOR_V1", "builder": ROOT / "tools" / "build_transition_set_enumerator_v1.py", "spec_path": ROOT / "spec" / "formulas" / "domains" / "portfolio.yaml", "schema_path": ROOT / "schemas" / "generated" / "transition_set_enumerator_v1.schema.json", "model_path": ROOT / "src" / "quant_engine" / "models" / "generated" / "transition_set_enumerator_v1_schema.py", "temp_path": ROOT / "Temp" / "transition_set_enumerator_v1.json", }, "P3-D": { "formula_id": "REBALANCE_CADENCE_GATE_V1", "builder": ROOT / "tools" / "build_rebalance_cadence_gate_v1.py", "spec_path": ROOT / "spec" / "formulas" / "domains" / "portfolio.yaml", "schema_path": ROOT / "schemas" / "generated" / "rebalance_cadence_gate_v1.schema.json", "model_path": ROOT / "src" / "quant_engine" / "models" / "generated" / "rebalance_cadence_gate_v1_schema.py", "temp_path": ROOT / "Temp" / "rebalance_cadence_gate_v1.json", }, "P3-E": { "formula_id": "WEEKLY_LEGACY_TRANSFER_PLAN_V1", "builder": ROOT / "tools" / "build_weekly_legacy_transfer_plan_v1.py", "spec_path": ROOT / "spec" / "formulas" / "domains" / "cash.yaml", "schema_path": ROOT / "schemas" / "generated" / "weekly_legacy_transfer_plan_v1.schema.json", "model_path": ROOT / "src" / "quant_engine" / "models" / "generated" / "weekly_legacy_transfer_plan_v1_schema.py", "temp_path": ROOT / "Temp" / "weekly_legacy_transfer_plan_v1.json", }, } def _read_text(path: Path) -> str: if not path.exists(): return "" return path.read_text(encoding="utf-8", errors="replace") def _read_json(path: Path) -> dict[str, Any]: if not path.exists(): return {} try: payload = json.loads(path.read_text(encoding="utf-8")) return payload if isinstance(payload, dict) else {} except Exception: return {} def _contains_all(text: str, needles: list[str]) -> bool: lower = text.lower() return all(needle.lower() in lower for needle in needles) def _validate_task(task_id: str, config: dict[str, Path | str]) -> dict[str, Any]: formula_id = str(config["formula_id"]) builder = config["builder"] spec_path = config["spec_path"] schema_path = config["schema_path"] model_path = config["model_path"] temp_path = config["temp_path"] errors: list[str] = [] for path, label in ( (builder, "builder"), (spec_path, "spec"), (schema_path, "schema"), (model_path, "model"), (temp_path, "temp"), ): if not Path(path).exists(): errors.append(f"missing_{label}:{Path(path).relative_to(ROOT)}") temp_doc = _read_json(Path(temp_path)) if temp_doc.get("formula_id") != formula_id: errors.append(f"temp_formula_id:{temp_doc.get('formula_id')!r}") if task_id == "P3-A" and float(temp_doc.get("state_vector_completeness_pct") or 0.0) < 0.0: errors.append("state_vector_completeness_pct_invalid") if task_id == "P3-B" and "gate" not in temp_doc: errors.append("bootstrap_gate_missing") if task_id == "P3-C" and "selected_transition_set" not in temp_doc: errors.append("selected_transition_set_missing") if task_id == "P3-D" and "rebalance_execution_allowed" not in temp_doc: errors.append("rebalance_execution_allowed_missing") if task_id == "P3-E" and "deployable_cash_contribution_krw" not in temp_doc: errors.append("deployable_cash_contribution_missing") return { "formula_id": formula_id, "builder_path": str(builder), "spec_path": str(spec_path), "schema_path": str(schema_path), "model_path": str(model_path), "temp_path": str(temp_path), "gate": "PASS" if not errors else "FAIL", "errors": errors, "temp_doc": temp_doc, } def main() -> int: plan_text = _read_text(PLAN_PATH) decision_flow_text = _read_text(DECISION_FLOW_PATH) manifest = _read_text(MANIFEST_PATH) plan_doc = yaml.safe_load(plan_text) if plan_text else {} task_results: dict[str, dict[str, Any]] = {} errors: list[str] = [] for task_id, config in TASKS.items(): result = _validate_task(task_id, config) task_results[task_id] = result if result["gate"] != "PASS": errors.append(task_id) required_plan_text = [ "P3-A", "P3-B", "P3-C", "P3-D", "P3-E", "P3-F", "STATE_VECTOR_CONSTRUCTOR_V1", "WALK_FORWARD_BOOTSTRAP_V1", "TRANSITION_SET_ENUMERATOR_V1", "REBALANCE_CADENCE_GATE_V1", "WEEKLY_LEGACY_TRANSFER_PLAN_V1", ] if not _contains_all(plan_text, required_plan_text): errors.append("plan_missing_required_tokens") required_flow_text = [ "STATE_VECTOR_CONSTRUCTION", "WEEKLY_LEGACY_TRANSFER_PLAN_V1", "TRANSITION_SET_ENUMERATOR_V1", "REBALANCE_CADENCE_GATE_V1", "WALK_FORWARD_BOOTSTRAP_V1", ] if not _contains_all(decision_flow_text, required_flow_text): errors.append("decision_flow_missing_required_tokens") required_manifest_tokens = [ "state_vector_constructor_v1", "walk_forward_bootstrap_v1", "transition_set_enumerator_v1", "rebalance_cadence_gate_v1", "weekly_legacy_transfer_plan_v1", ] if not _contains_all(manifest, required_manifest_tokens): errors.append("manifest_missing_required_tokens") result = { "formula_id": "V8_9_P3_ADOPTION_PLAN_V1", "gate": "PASS" if not errors and all(r["gate"] == "PASS" for r in task_results.values()) else "FAIL", "plan_path": str(PLAN_PATH), "decision_flow_path": str(DECISION_FLOW_PATH), "manifest_path": str(MANIFEST_PATH), "task_results": task_results, "errors": errors, } out = ROOT / "Temp" / "v8_9_p3_adoption_plan_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 result["gate"] == "PASS" else 1 if __name__ == "__main__": raise SystemExit(main())