from __future__ import annotations import csv import json import subprocess import sys import zipfile from pathlib import Path ROOT = Path(__file__).resolve().parents[1] CONFIG = json.loads((ROOT / "src" / "task_config.json").read_text(encoding="utf-8")) def require(path: Path) -> None: if not path.exists(): raise SystemExit(f"missing required file: {path}") def assert_contains(path: Path, needle: str, error: str) -> None: if needle not in path.read_text(encoding="utf-8"): raise SystemExit(error) def main() -> int: archive_path = ROOT / "public" / CONFIG["archive_name"] bundle_dir = ROOT / "public" / "mirrortrace_casebundle" retained_copy = bundle_dir / "20_artifacts" / CONFIG["retained_copy_name"] require(ROOT / "public" / "case_brief.txt") require(ROOT / "public" / "negotiation_excerpt.txt") require(archive_path) require(bundle_dir / "10_datasets" / "graph_nodes.csv") require(bundle_dir / "10_datasets" / "graph_edges.csv") require(bundle_dir / "20_artifacts" / "doc_01.pdf") require(bundle_dir / "20_artifacts" / "doc_03.pdf") require(bundle_dir / "20_artifacts" / "capture_05.html") require(bundle_dir / "20_artifacts" / "capture_11.html") require(retained_copy) assert_contains( ROOT / "public" / "case_brief.txt", CONFIG["retained_copy_name"], "case_brief.txt is missing retained copy reference", ) doc_01_bytes = (bundle_dir / "20_artifacts" / "doc_01.pdf").read_bytes() if f"/Author ({CONFIG['correct']['pdf_author']})".encode("ascii") not in doc_01_bytes: raise SystemExit("doc_01.pdf is missing expected metadata author") if b"Retained legal copy marker: ED-C0" not in doc_01_bytes: raise SystemExit("doc_01.pdf is missing expected retained copy marker") doc_03_bytes = (bundle_dir / "20_artifacts" / "doc_03.pdf").read_bytes() if f"/Author ({CONFIG['same_cluster_decoy']['pdf_author']})".encode("ascii") not in doc_03_bytes: raise SystemExit("doc_03.pdf is missing expected same-cluster decoy author") capture_05 = (bundle_dir / "20_artifacts" / "capture_05.html").read_text(encoding="utf-8") if CONFIG["correct"]["full_name"] not in capture_05: raise SystemExit("capture_05.html is missing expected identity") capture_11 = (bundle_dir / "20_artifacts" / "capture_11.html").read_text(encoding="utf-8") if CONFIG["same_cluster_decoy"]["personal_domain"] not in capture_11: raise SystemExit("capture_11.html is missing expected same-cluster decoy pivot") with (bundle_dir / "10_datasets" / "graph_edges.csv").open(encoding="utf-8", newline="") as fh: rows = list(csv.DictReader(fh)) expected_edges = { ("panel", "cert_ms441"), ("helpdesk_mercury", "doc_03"), ("git_mercury", "personal_vk"), ("helpdesk_mercury", "personal_as"), } actual_edges = {(row["source"], row["target"]) for row in rows} if not expected_edges.issubset(actual_edges): raise SystemExit("graph_edges.csv is missing expected hard-mode relations") subprocess.run( ["unzip", "-P", CONFIG["passphrase"], "-t", str(retained_copy)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) with zipfile.ZipFile(archive_path) as zf: expected = { "mirrortrace_casebundle/00_brief/case_brief.txt", "mirrortrace_casebundle/10_datasets/graph_nodes.csv", "mirrortrace_casebundle/10_datasets/graph_edges.csv", "mirrortrace_casebundle/20_artifacts/doc_01.pdf", "mirrortrace_casebundle/20_artifacts/doc_03.pdf", "mirrortrace_casebundle/20_artifacts/capture_05.html", "mirrortrace_casebundle/20_artifacts/capture_11.html", f"mirrortrace_casebundle/20_artifacts/{CONFIG['retained_copy_name']}", } names = set(zf.namelist()) if not expected.issubset(names): raise SystemExit("participant archive is missing expected hard-mode files") print("MirrorTrace offline release check passed.") return 0 if __name__ == "__main__": sys.exit(main())