#!/usr/bin/env python3 from __future__ import annotations import json import re import sys from pathlib import Path import yaml ROOT = Path(__file__).resolve().parent.parent # Whitelist of package.json scripts that are dev servers, system tasks, or release DAG wrappers WHITELIST = { "ops:validate", "ops:release", "ops:dev", "ops:snapshot-web", "ops:postgres-stub", "ops:clean", "full-gate", "validate-engine-strict", "validate-engine-integrity", "prepare-upload-zip", "ops:package", "ops:data-collect", "ops:sell-eval", "ops:sell-validate", "ops:snapshot-validate", "ops:snapshot-web-validate", "ops:calibration-backlog", "ops:sector-refresh", "ops:sector-refresh-apply", "ops:sector-workbook", "ops:audit", "validate-calibration-change-ledger", "validate-gas-recovery", "validate-behavioral-coverage" } def extract_tools_from_script(script_cmd: str) -> list[str]: # Find all python tools/*.py references in the command string matches = re.findall(r"tools/[A-Za-z0-9_]+\.py", script_cmd) # Also find if it directly calls python src/... files matches.extend(re.findall(r"src/[A-Za-z0-9_/]+\.py", script_cmd)) return [m.replace("\\", "/") for m in matches] def main() -> int: package_path = ROOT / "package.json" dag_path = ROOT / "spec" / "41_release_dag.yaml" if not package_path.exists(): print(f"package.json missing at {package_path}") return 1 if not dag_path.exists(): print(f"release_dag missing at {dag_path}") return 1 # Load package.json scripts try: package_data = json.loads(package_path.read_text(encoding="utf-8")) scripts = package_data.get("scripts", {}) except Exception as e: print(f"Failed to parse package.json: {e}") return 1 # Load DAG nodes try: dag_data = yaml.safe_load(dag_path.read_text(encoding="utf-8")) or {} nodes = dag_data.get("dag", {}).get("nodes", {}) except Exception as e: print(f"Failed to parse release_dag YAML: {e}") return 1 # Collect all python scripts called by DAG nodes dag_commands = set() for nid, node in nodes.items(): cmd_list = node.get("command", []) for chunk in cmd_list: if chunk.startswith("tools/") or chunk.startswith("src/"): dag_commands.add(chunk.replace("\\", "/")) # Track orphans and mismatch orphan_scripts = [] for script_name, cmd in scripts.items(): if script_name in WHITELIST: continue referenced_tools = extract_tools_from_script(cmd) if not referenced_tools: # Not a tool execution script, skip continue # Check if all tools executed by this script are in the DAG for tool in referenced_tools: if tool not in dag_commands: print(f"DEBUG: Orphan script '{script_name}' calls tool '{tool}' not registered in DAG") orphan_scripts.append((script_name, tool)) orphan_script_count = len(orphan_scripts) dag_node_count = len(nodes) # All DAG nodes are executed via run_release_dag_v3.py under "full-gate" package_script_reachable_node_count = dag_node_count gate_passed = (orphan_script_count == 0) result = { "formula_id": "RELEASE_DAG_CONTRACT_VALIDATOR_V1", "dag_node_count": dag_node_count, "package_script_reachable_node_count": package_script_reachable_node_count, "orphan_script_count": orphan_script_count, "orphan_details": orphan_scripts, "gate": "PASS" if gate_passed else "FAIL" } out_dir = ROOT / "Temp" out_dir.mkdir(parents=True, exist_ok=True) out_path = out_dir / "release_dag_contract_validation_v1.json" out_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8") print(json.dumps(result, ensure_ascii=True, indent=2)) return 0 if gate_passed else 1 if __name__ == "__main__": sys.exit(main())