#!/usr/bin/env python3 """Validate claims.yaml against its JSON Schema and check referenced paths. Usage (CI or local): python3 scripts/math-first/validate_claims_registry.py Validates: 1. claims.yaml parses as valid YAML 2. Contents match shared-data/schemas/claims-registry.schema.json 3. Every repo-relative path referenced in claims.yaml actually exists on disk Exits 0 on success, 1 on any failure. """ from __future__ import annotations import json import sys from pathlib import Path try: import yaml except ImportError: print("SKIP: PyYAML not installed (pip install PyYAML)") sys.exit(0) try: from jsonschema import Draft202012Validator except ImportError: print("SKIP: jsonschema not installed (pip install 'jsonschema>=4.21')") sys.exit(0) REPO_ROOT = Path(__file__).resolve().parents[2] CLAIMS_PATH = REPO_ROOT / "claims.yaml" SCHEMA_PATH = REPO_ROOT / "shared-data" / "schemas" / "claims-registry.schema.json" def main() -> int: if not CLAIMS_PATH.exists(): print("SKIP: claims.yaml not found") return 0 if not SCHEMA_PATH.exists(): print(f"SKIP: schema not found at {SCHEMA_PATH}") return 0 # Parse YAML try: data = yaml.safe_load(CLAIMS_PATH.read_text()) except yaml.YAMLError as exc: print(f"FAIL: claims.yaml is not valid YAML — {exc}") return 1 # Validate against schema schema = json.loads(SCHEMA_PATH.read_text()) validator = Draft202012Validator(schema) errors = list(validator.iter_errors(data)) if errors: print("FAIL: claims.yaml schema validation errors:") for err in errors: print(f" {err.json_path}: {err.message}") return 1 print("OK claims.yaml matches schema") # Check referenced paths exist missing = 0 claims = data.get("claims", []) for claim in claims: cid = claim.get("id", "") # Check lean path lean_path = claim.get("lean") if lean_path and not (REPO_ROOT / lean_path).exists(): print(f"WARN {cid}: lean path does not exist — {lean_path}") # Warn only; the file might be in a different branch # Check review receipt paths for rpath in claim.get("review_receipts", []): if not (REPO_ROOT / rpath).exists(): print(f"FAIL {cid}: receipt not found — {rpath}") missing += 1 # Check source paths (skip external citations) for spath in claim.get("sources", []): if spath.startswith("http://") or spath.startswith("https://"): continue if not (REPO_ROOT / spath).exists(): print(f"WARN {cid}: source not found — {spath}") if missing: print(f"\n{missing} referenced receipt(s) not found on disk", file=sys.stderr) return 1 print(f"\n{len(claims)} claim(s) validated OK") return 0 if __name__ == "__main__": raise SystemExit(main())