1"""Freeze manifest: SHA-256 of every public bundle file, every oracle file, the arm texts, the harness files that affect outcomes,2the preregistration and the judge prompt builder. Written before the pilot; any later change is an amendment.3Usage: .venv/bin/python scripts/freeze.py --tasks ACC-01 ... --out protocol/preregistration/freeze_manifest.json"""4from __future__ import annotations5import argparse, hashlib, json, os, subprocess, sys, datetime as dt6REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))7ORACLES = os.path.expanduser("~/wp42-oracles")89def sha(p):10 h = hashlib.sha256()11 with open(p, "rb") as f:12 for ch in iter(lambda: f.read(1 << 20), b""): h.update(ch)13 return h.hexdigest()1415def tree(root, skip=("__pycache__", ".DS_Store")):16 out = {}17 for dp, dns, fns in os.walk(root):18 dns[:] = [d for d in dns if d not in skip]19 for fn in sorted(fns):20 if fn in skip: continue21 p = os.path.join(dp, fn); out[os.path.relpath(p, root)] = sha(p)22 return out2324if __name__ == "__main__":25 ap = argparse.ArgumentParser(); ap.add_argument("--tasks", nargs="+", required=True); ap.add_argument("--out", default=os.path.join(REPO, "protocol", "preregistration", "freeze_manifest.json"))26 a = ap.parse_args()27 man = {"frozen_utc": dt.datetime.now(dt.timezone.utc).isoformat(), "git_head": subprocess.run(["git", "rev-parse", "HEAD"], cwd=REPO, capture_output=True, text=True).stdout.strip() or None,28 "scenarios": {}, "oracles": {}, "harness": {}, "protocol": {}}29 for t in a.tasks:30 man["scenarios"][t] = tree(os.path.join(REPO, "benchmark", "scenarios", t))31 od = os.path.join(ORACLES, t); man["oracles"][t] = {k: v for k, v in tree(od).items() if not k.startswith("validation/")}32 for rel in ["src/wp42/arms.py", "src/wp42/launcher.py", "src/wp42/bundle.py", "src/wp42/scheduler.py", "src/wp42/judge.py", "src/wp42/evaluate.py", "src/wp42/metrics.py", "src/wp42/config.py", "src/wp42/portal.py", "src/wp42/office.py", "benchmark/tools/selfcheck.py", "benchmark/validators/common.py", "benchmark/BRIEF_FOOTER.md", "benchmark/COMPLETION_SCHEMA.json"]:33 man["harness"][rel] = sha(os.path.join(REPO, rel))34 for rel in ["protocol/preregistration/PREREGISTRATION.md", "RESEARCH_PLAN.md", "analysis/econometrics/estimate.py"]:35 man["protocol"][rel] = sha(os.path.join(REPO, rel))36 man["claude_code_controller"] = subprocess.run(["claude", "--version"], capture_output=True, text=True).stdout.strip()37 json.dump(man, open(a.out, "w"), indent=1); print("frozen", a.out, "scenarios:", len(man["scenarios"]))38