#!/usr/bin/env python3 """ Verify graph-40k fixture hashes and counts against manifest.json. """ from __future__ import annotations import hashlib import json import sys from pathlib import Path from typing import Tuple def sha256(path: Path) -> str: h = hashlib.sha256() with path.open("rb") as f: for chunk in iter(lambda: f.read(8192), b""): h.update(chunk) return h.hexdigest() def count_lines(path: Path) -> int: with path.open("r", encoding="utf-8") as f: return sum(1 for _ in f if _.strip()) def verify(manifest_path: Path) -> Tuple[bool, str]: manifest = json.loads(manifest_path.read_text()) base = manifest_path.parent nodes_path = base / "nodes.ndjson" edges_path = base / "edges.ndjson" overlay_path = base / manifest["overlay"]["path"] checks = [ ("nodes hash", sha256(nodes_path) == manifest["hashes"]["nodes_ndjson_sha256"]), ("edges hash", sha256(edges_path) == manifest["hashes"]["edges_ndjson_sha256"]), ("overlay hash", sha256(overlay_path) == manifest["hashes"]["overlay_ndjson_sha256"]), ("nodes count", count_lines(nodes_path) == manifest["counts"]["nodes"]), ("edges count", count_lines(edges_path) == manifest["counts"]["edges"]), ("overlay count", count_lines(overlay_path) == manifest["counts"]["overlays"]["policy.overlay.v1"]), ] failed = [name for name, ok in checks if not ok] if failed: return False, f"Failed checks: {', '.join(failed)}" return True, "All hashes and counts match manifest." def main() -> int: manifest_path = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path(__file__).resolve().parent / "manifest.json" ok, message = verify(manifest_path) print(message) return 0 if ok else 1 if __name__ == "__main__": raise SystemExit(main())