test: final source recheck for V1 jet CV and V3 Taylor-Green runs
Both runners now re-derive the recorded source identity after the run (source_provenance.final_source_check) and only then mark the receipt complete. A changed source, or a recheck that fails, rewrites the receipt as invalid and raises: - V1: in-run receipts carry run_status=INCOMPLETE; finish_receipt sets COMPLETE or INVALID_SOURCE_CHANGED (+ source_recheck/source_after). - V3: completed stays False, invalid=SOURCE_CHANGED_DURING_RUN, no screening_pass; the assessor requires source_recheck UNCHANGED (frozen mode still accepts receipts from before the field existed). Interrupted runs keep INCOMPLETE / completed=False. Host tests cover unchanged, changed and failed rechecks and incomplete runs. No threshold or solver change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8cae58cdf1
commit
df0f586331
7 files changed
+159
-15
No files matched your search
@@ -196,5 +196,40 @@ class Host(unittest.TestCase):
|
||||
self.assertTrue(record["src_sha256"])
|
||||
|
||||
|
||||
def test_final_source_recheck_completes_or_invalidates_receipt(self):
|
||||
import json
|
||||
import tempfile
|
||||
import run_jet_circle_cv as v
|
||||
recorded = dict(source_mode="current_checkout", checkout_revision="a" * 40,
|
||||
checkout_dirty=False, src_tree="b" * 40)
|
||||
payload = dict(status="PILOT_DIAGNOSTIC_ONLY", run_status="INCOMPLETE", windows=[1, 2])
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
path = Path(d) / "re100_s1.json"
|
||||
path.write_text(json.dumps(payload))
|
||||
done = v.finish_receipt(path, payload, recorded, lambda: dict(recorded))
|
||||
self.assertEqual(json.loads(path.read_text()), done)
|
||||
self.assertEqual((done["run_status"], done["source_recheck"]), ("COMPLETE", "UNCHANGED"))
|
||||
for recheck, after in ((lambda: dict(recorded, checkout_dirty=True), True),
|
||||
(lambda: v.verify(Path(d)), None)):
|
||||
path.write_text(json.dumps(payload))
|
||||
with self.assertRaisesRegex(RuntimeError, "Source changed during the run"):
|
||||
v.finish_receipt(path, payload, recorded, recheck)
|
||||
bad = json.loads(path.read_text())
|
||||
self.assertEqual((bad["run_status"], bad["source_recheck"]),
|
||||
("INVALID_SOURCE_CHANGED", "CHANGED"))
|
||||
self.assertEqual(bad["source_after"] is None, after is None)
|
||||
self.assertEqual(bad["windows"], [1, 2])
|
||||
self.assertFalse(Path(str(path) + ".tmp").exists())
|
||||
|
||||
def test_receipts_written_during_the_run_are_incomplete(self):
|
||||
"""Static check (run_case needs CUDA): the in-run payload is INCOMPLETE and only
|
||||
main() -> finish_receipt can mark it COMPLETE."""
|
||||
import inspect
|
||||
import run_jet_circle_cv as v
|
||||
self.assertIn('run_status="INCOMPLETE"', inspect.getsource(v.run_case))
|
||||
self.assertNotIn("COMPLETE\"", inspect.getsource(v.run_case).replace("INCOMPLETE\"", ""))
|
||||
self.assertIn("finish_receipt(", inspect.getsource(v.main))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -111,3 +111,20 @@ def test_historical_files_reports_unreadable_blob_as_history_unavailable(tmp_pat
|
||||
with pytest.raises(HistoryUnavailable, match="blob not in this clone"):
|
||||
source_provenance.historical_files(ROOT, ORIGIN, ["src/a.py"], tmp_path)
|
||||
assert not (tmp_path / "src/a.py").exists()
|
||||
|
||||
|
||||
def test_final_source_check_unchanged_changed_and_failed_recheck():
|
||||
from source_provenance import final_source_check
|
||||
|
||||
recorded = dict(revision="a" * 40, dirty=False)
|
||||
assert final_source_check(recorded, lambda: dict(recorded)) == dict(source_recheck="UNCHANGED")
|
||||
changed = final_source_check(recorded, lambda: dict(recorded, dirty=True))
|
||||
assert changed == dict(source_recheck="CHANGED", source_after=dict(recorded, dirty=True),
|
||||
source_recheck_error=None)
|
||||
|
||||
def fails():
|
||||
raise RuntimeError("Source hash mismatch: src/x.py")
|
||||
|
||||
failed = final_source_check(recorded, fails)
|
||||
assert (failed["source_recheck"], failed["source_after"]) == ("CHANGED", None)
|
||||
assert "Source hash mismatch" in failed["source_recheck_error"]
|
||||
Reference in new issue
Block a user