#!/usr/bin/env python3 import sys import argparse from pathlib import Path import yaml ROOT = Path(__file__).resolve().parents[1] def main(): parser = argparse.ArgumentParser() parser.add_argument("--dir", default="governance/change_requests") parser.add_argument("--strict", action="store_true") args = parser.parse_args() dir_path = ROOT / args.dir if not dir_path.exists(): print(f"Directory not found: {dir_path}") sys.exit(1) yaml_files = list(dir_path.glob("*.yaml")) if not yaml_files: print("No change request files found") sys.exit(0) required_fields = ["why", "expected_edge", "risk", "data_dependency", "tests", "rollback_plan"] unowned_change_count = 0 missing_rollback_plan_count = 0 for path in yaml_files: try: data = yaml.safe_load(path.read_text(encoding="utf-8")) except Exception as e: print(f"Failed to parse {path.name}: {e}") sys.exit(1) # Check metadata meta = data.get("proposal_metadata") or {} if not meta.get("author"): unowned_change_count += 1 # Check rationale, dependencies, verification rationale = data.get("rationale") or {} deps = data.get("dependencies") or {} verify = data.get("verification") or {} all_fields = { "why": rationale.get("why"), "expected_edge": rationale.get("expected_edge"), "risk": rationale.get("risk"), "data_dependency": deps.get("data_dependency"), "tests": verify.get("tests"), "rollback_plan": verify.get("rollback_plan") } for f in required_fields: if not all_fields.get(f): if f == "rollback_plan": missing_rollback_plan_count += 1 else: print(f"Missing required field in {path.name}: {f}") sys.exit(1) if unowned_change_count > 0: print(f"Validation failed: {unowned_change_count} change requests without author") sys.exit(1) if missing_rollback_plan_count > 0: print(f"Validation failed: {missing_rollback_plan_count} change requests without rollback_plan") sys.exit(1) print("VALIDATION OK") sys.exit(0) if __name__ == "__main__": main()