From aceea70e5f9cfb37cfbec9eafa21fd128b61c664 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 18:16:21 +0000 Subject: [PATCH] test: record checkout provenance in DFG Re20 receipts plan.json and result.json now carry HEAD, the dirty flag, the src tree hash and the runner hash, labelled current_checkout; result.json is refused if the checkout changed during the run. Numerics, sampling and the frozen steady rule are unchanged. Co-Authored-By: Claude Opus 5.5 --- tests/unit/test_dfg_re20.py | 10 ++++++++++ tests/validation/run_dfg_re20.py | 20 ++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_dfg_re20.py b/tests/unit/test_dfg_re20.py index 05f8800..ec7b685 100644 --- a/tests/unit/test_dfg_re20.py +++ b/tests/unit/test_dfg_re20.py @@ -75,5 +75,15 @@ class Re20Contract(unittest.TestCase): self.assertEqual((out["status"], out["verdict"]), ("INCOMPLETE", None)) +class Re20Provenance(unittest.TestCase): + def test_records_current_checkout(self): + record = v.provenance(rr.DEFAULT_SOURCE) + self.assertEqual(record["source_mode"], "current_checkout") + self.assertEqual(len(record["checkout_revision"]), 40) + self.assertEqual(len(record["src_tree"]), 40) + self.assertEqual(len(record["runner_sha256"]), 64) + self.assertIn("checkout_dirty", record) + + if __name__ == "__main__": unittest.main() diff --git a/tests/validation/run_dfg_re20.py b/tests/validation/run_dfg_re20.py index fdf43b3..9700042 100644 --- a/tests/validation/run_dfg_re20.py +++ b/tests/validation/run_dfg_re20.py @@ -194,6 +194,17 @@ def plan(collision="SRT"): label="compared against the official reference bands (non-canonical neq_extrap outlet, zou_he_local inlet); not a full benchmark certification") +def provenance(source): + """Current checkout identity: HEAD, dirty flag, src tree and this runner's hash.""" + import hashlib + import subprocess + from source_provenance import checkout_provenance + tree = subprocess.check_output(["git", "-C", str(source), "rev-parse", "HEAD:src"], + text=True).strip() + return dict(source_mode="current_checkout", **checkout_provenance(source), src_tree=tree, + runner_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest()) + + def main(): parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--case", choices=["refined", "uniform", "both"], default="refined") @@ -204,8 +215,9 @@ def main(): parser.add_argument("--max-wall-seconds", type=float) a = parser.parse_args() a.source = a.source.resolve() + record = dict(plan(a.collision), provenance=provenance(a.source)) a.output.mkdir(parents=True, exist_ok=False) - (a.output / "plan.json").write_text(json.dumps(plan(a.collision), indent=2, allow_nan=False)) + (a.output / "plan.json").write_text(json.dumps(record, indent=2, allow_nan=False)) if a.plan: return if not a.max_wall_seconds or a.max_wall_seconds <= 0 or not os.environ.get("CUDA_VISIBLE_DEVICES"): @@ -215,7 +227,11 @@ def main(): a.deadline = time.monotonic() + a.max_wall_seconds cases = ["refined", "uniform"] if a.case == "both" else [a.case] results = [run_case(a, h, case) for case in cases] - (a.output / "result.json").write_text(json.dumps(results, indent=2, allow_nan=False)) + after = provenance(a.source) + if after != record["provenance"]: + raise RuntimeError("Source checkout changed during the run") + (a.output / "result.json").write_text(json.dumps(dict(provenance=after, cases=results), + indent=2, allow_nan=False)) if __name__ == "__main__":