1"""Artifact audit: every raw record validates against the schema; every table/figure has a provenance entry; no secrets in the tree."""2from __future__ import annotations3import json, re, glob4from pathlib import Path5from .util import ROOT, read_jsonl6from .records import validate_record78SECRET_RE = re.compile(r"(sk-ant-[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9]{32,}|hf_[A-Za-z0-9]{20,})")910def main(root: str) -> int:11 root = Path(root).resolve(); ok = True12 n = bad = 013 for p in glob.glob(str(root / "runs/raw/*.jsonl")):14 if p.endswith("E6_network.jsonl") or "E6_" in p: continue15 for r in read_jsonl(p):16 n += 117 if validate_record(r): bad += 118 print(f"records: {n} total, {bad} invalid"); ok &= bad == 019 leaks = []20 for p in glob.glob(str(root / "**/*"), recursive=True):21 pp = Path(p)22 if pp.is_dir() or ".venv" in p or ".git/" in p or pp.suffix in (".npz", ".safetensors", ".pdf", ".png", ".parquet", ".pyc"): continue23 try:24 t = pp.read_text(errors="ignore")25 except Exception:26 continue27 if SECRET_RE.search(t): leaks.append(str(pp.relative_to(root)))28 print("secret-like strings in tree:", leaks); ok &= not leaks29 prov = root / "results/provenance.json"30 if prov.exists():31 pv = json.load(open(prov))32 missing = [k for k, v in pv.items() if not (root / k).exists()]33 print(f"provenance entries: {len(pv)}, missing artifacts: {missing}"); ok &= not missing34 else:35 print("no results/provenance.json yet")36 return 0 if ok else 137