Archive MaleCNS research sources, provenance, bibliography and immutable data inventory
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate repository navigation and original evidence, create a NEW receipt."""
|
||||
from datetime import datetime, timezone
|
||||
import gzip
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
root = Path.cwd().resolve()
|
||||
def sha(p):
|
||||
h = hashlib.sha256()
|
||||
with p.open("rb") as f:
|
||||
for b in iter(lambda:f.read(4*1024*1024), b""):
|
||||
h.update(b)
|
||||
return h.hexdigest()
|
||||
|
||||
errors = []
|
||||
cat = root / "registry/catalog-20260929"
|
||||
copies = json.loads((cat / "evidence-index.json").read_text())
|
||||
for r in copies:
|
||||
if sha(root / r["source_path"]) != r["sha256"] or sha(cat / r["copy_path"]) != r["sha256"]:
|
||||
errors.append("evidence checksum: " + r["source_path"])
|
||||
|
||||
links = 0
|
||||
# New navigation only; old historical reports keep their original links unchanged.
|
||||
pages = [root/"README.md", root/"NOTICE.md", *sorted((root/"docs").rglob("*.md")), root/"references/README.md", root/"references/evidence-map.md"]
|
||||
for p in pages:
|
||||
for link in re.findall(r"\]\(([^)]+)\)", p.read_text(encoding="utf-8")):
|
||||
if re.match(r"[a-z]+://|#", link):
|
||||
continue
|
||||
links += 1
|
||||
if not (p.parent / link.split("#",1)[0]).exists():
|
||||
errors.append("missing link: " + str(p.relative_to(root)) + " -> " + link)
|
||||
|
||||
papers = json.loads((root/"references/papers.json").read_text())["papers"]
|
||||
for p in papers:
|
||||
if p["status"] != "VERIFIED-METADATA" or sha(root/"references"/p["metadata_file"]) != p["metadata_sha256"]:
|
||||
errors.append("bibliography metadata: " + p["doi"])
|
||||
snapshot = root/"registry/snapshots/bootstrap-20260929/files.jsonl.gz"
|
||||
with gzip.open(snapshot, "rt", encoding="utf-8") as f:
|
||||
originals = {r["path"]:r for r in map(json.loads, f)}
|
||||
tracked = subprocess.check_output(["git", "ls-files", "-z"]).decode().split("\0")
|
||||
models = 0
|
||||
for path in filter(None, tracked):
|
||||
if path.startswith("models/"):
|
||||
models += 1
|
||||
old = originals.get(path)
|
||||
blob = subprocess.check_output(["git", "show", ":"+path])
|
||||
if old is None or hashlib.sha256(blob).hexdigest() != old.get("sha256"):
|
||||
errors.append("staged original model differs: " + path)
|
||||
verification = json.loads((root/"registry/bootstrap-verification.json").read_text())
|
||||
if not verification["pass"]:
|
||||
errors.append("full scientific snapshot verification failed")
|
||||
guard = subprocess.run([sys.executable, "-B", "tools/repo_guard.py"], capture_output=True, text=True)
|
||||
if guard.returncode:
|
||||
errors.append("staged content guard failed")
|
||||
tests = subprocess.run([sys.executable, "-B", "tools/test_repository_audit.py"], capture_output=True, text=True)
|
||||
if tests.returncode:
|
||||
errors.append("tool unit tests failed")
|
||||
receipt = {"pass":not errors, "errors":errors, "checked_utc":datetime.now(timezone.utc).isoformat(),
|
||||
"evidence_copies_verified":len(copies), "original_model_blobs_verified":models,
|
||||
"navigation_local_links_verified":links, "papers_metadata_verified":len(papers),
|
||||
"scientific_files_verified":verification["checked"], "staged_guard":json.loads(guard.stdout),
|
||||
"tool_tests_exit_code":tests.returncode,
|
||||
"scientific_regressions_reexecuted":False,
|
||||
"data_deleted_moved_or_rewritten":False,
|
||||
"permissions_change":{"scope":"directory ownership only, nonrecursive",
|
||||
"paths":["/data/digital-drosophila","/data/digital-drosophila/docs","/data/digital-drosophila/tools"],
|
||||
"before":"root:root", "after":"qishizhe:qishizhe"},
|
||||
"large_data_pushed":False, "offsite_full_data_backup_created":False}
|
||||
out = root/"registry/repository-validation.json"
|
||||
with out.open("x", encoding="utf-8") as f:
|
||||
json.dump(receipt, f, ensure_ascii=False, indent=2)
|
||||
f.write("\n")
|
||||
print(json.dumps(receipt, ensure_ascii=False, indent=2))
|
||||
sys.exit(int(bool(errors)))
|
||||
Reference in New Issue
Block a user