#!/usr/bin/env python3 """Independently verify Visual-only frontmatter changes against the original private bundle.""" import argparse import hashlib import json import os import re from pathlib import Path def read(root, name): path = root / name if ( Path(name).is_absolute() or ".." in Path(name).parts or any(p.is_symlink() for p in (path, *path.parents)) ): raise ValueError("unsafe source path") info = path.stat() if ( not path.is_file() or info.st_uid != os.getuid() or info.st_mode & 0o077 or info.st_nlink != 1 or info.st_size > 16 * 1024 * 1024 ): raise ValueError("private bounded source required") return path.read_bytes() def verify(source, out): if not __debug__: raise ValueError("verification assertions required") old = json.loads(read(source, "reconciliation.json")) new = json.loads(read(out, "reconciliation.json")) def sha(raw): return hashlib.sha256(raw).hexdigest() assert new["original_report_sha256"] == sha(read(source, "reconciliation.json")) assert read(out, "source-capture/reconciliation-before-visuals.json") == read( source, "reconciliation.json" ) for name, digest in new["file_sha256"].items(): assert sha(read(out, name)) == digest changed = 0 for name, digest in old["file_sha256"].items(): original = read(source, name) current = read(out, name) assert sha(original) == digest if current != original: assert "/pages/" in name and name.endswith(".md") before, body = original.split(b"\n---\n", 1) after, after_body = current.split(b"\n---\n", 1) assert body == after_body assert ( len(re.findall(rb'^visual: "assets/[a-f0-9]{64}\.(?:png|jpg|webp)"$', after, re.M)) == 1 ) if name.endswith("/index.md"): restored = re.sub( rb'\nvisual: "assets/[a-f0-9]{64}\.(?:png|jpg|webp)"$', b"", after ) else: restored = re.sub( rb'^visual: "assets/[a-f0-9]{64}\.(?:png|jpg|webp)"$', b'visual: ""', after, flags=re.M, ) assert restored == before changed += 1 for axis in ["unassigned_article_ids", "source_sha256"]: assert new[axis] == old[axis] assert len(old["spaces"]) == len(new["spaces"]) for a, b in zip(old["spaces"], new["spaces"], strict=True): assert a["blockers"] == b["blockers"] and not b["import_ready"] for key in a: if key != "articles": assert a[key] == b[key], key for x, y in zip(a["articles"], b["articles"], strict=True): assert {k: v for k, v in x.items() if k != "file_sha256"} == { k: v for k, v in y.items() if k != "file_sha256" } for p in out.rglob("*"): assert p.stat().st_mode & 0o077 == 0 return { "schema": "coulomb.visual-preservation-proof.v1", "verified_output_files": len(new["file_sha256"]), "verified_original_files": len(old["file_sha256"]), "frontmatter_only_changes": changed, "all_source_bodies_and_dates_preserved": True, "spaces_preserved": len(new["spaces"]), "original_blockers_preserved": True, "unassigned_records_preserved": True, "private_modes_verified": True, "private_bundle_sha256": sha(read(out, "reconciliation.json")), "production_imported": False, } def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("original", type=Path) parser.add_argument("prepared", type=Path) args = parser.parse_args() try: print(json.dumps(verify(args.original, args.prepared))) except Exception: print("Private visual preservation verification failed; no acceptance inferred.") return 1 return 0 if __name__ == "__main__": raise SystemExit(main())