#!/usr/bin/env python3 from __future__ import annotations import argparse import json import re import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] DEFAULT_PACKET = ROOT / "Temp" / "final_decision_packet_active.json" DEFAULT_REPORT = ROOT / "Temp" / "operational_report.json" def _load_json(path: Path) -> dict: return json.loads(path.read_text(encoding="utf-8")) def _find_section(report: dict, name: str) -> str: for section in report.get("sections", []): if section.get("name") == name: return str(section.get("markdown") or "") return "" def _extract_table_value(markdown: str, label: str) -> str | None: pattern = re.compile(rf"^\|\s*{re.escape(label)}\s*\|\s*([^|]+?)\s*\|", re.MULTILINE) match = pattern.search(markdown) if not match: return None return match.group(1).strip() def _as_number(value: str | None) -> float | None: if value is None: return None cleaned = value.replace(",", "").replace("%", "").strip() try: return float(cleaned) except Exception: return None def _compare_numeric(errors: list[str], label: str, packet_value: float | int | None, report_value: str | None, tolerance: float = 0.01) -> None: report_number = _as_number(report_value) if packet_value is None or report_number is None: errors.append(f"{label} missing comparison value") return if abs(float(packet_value) - report_number) > tolerance: errors.append(f"{label} mismatch: packet={packet_value} report={report_number}") def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--packet", default=str(DEFAULT_PACKET)) ap.add_argument("--report", default=str(DEFAULT_REPORT)) ap.add_argument("--strict", action="store_true") args = ap.parse_args() packet_path = Path(args.packet) report_path = Path(args.report) packet = _load_json(packet_path) report = _load_json(report_path) errors: list[str] = [] pass_100 = packet.get("pass_100") or {} exec_readiness = packet.get("execution_readiness") or {} prediction = packet.get("prediction") or {} pass_100_section = _find_section(report, "pass_100_criteria") exec_section = _find_section(report, "execution_readiness_matrix") pred_section = _find_section(report, "prediction_evaluation_improvement_report") _compare_numeric(errors, "PASS_100 score_0_100", pass_100.get("score_0_100"), _extract_table_value(pass_100_section, "score_0_100")) if str(pass_100.get("gate") or "") != str(_extract_table_value(pass_100_section, "게이트")): errors.append(f"PASS_100 gate mismatch: packet={pass_100.get('gate')} report={_extract_table_value(pass_100_section, '게이트')}") _compare_numeric(errors, "execution_readiness min_axis_score", exec_readiness.get("min_axis_score"), _extract_table_value(exec_section, "min_axis_score")) if str(exec_readiness.get("gate") or "") != str(_extract_table_value(exec_section, "게이트")): errors.append(f"execution_readiness gate mismatch: packet={exec_readiness.get('gate')} report={_extract_table_value(exec_section, '게이트')}") _compare_numeric(errors, "prediction match_rate_pct", prediction.get("match_rate_pct"), _extract_table_value(pred_section, "일치율")) result = { "packet": str(packet_path), "report": str(report_path), "gate": "PASS" if not errors else "FAIL_BLOCK_PUBLISH", "mismatch_count": len(errors), "mismatches": errors, } print(json.dumps(result, ensure_ascii=False, indent=2)) return 0 if not errors else 1 if __name__ == "__main__": raise SystemExit(main())