From 528ab8d1b5da59f8b37a39284844805fad82529f Mon Sep 17 00:00:00 2001 From: SamGu-NRX Date: Thu, 22 Oct 2026 14:05:56 -0500 Subject: [PATCH] fix(backend): port cold restart receipt validation Co-Authored-By: Claude Opus 5.5 --- backend/scripts/capture_cold_restart.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/backend/scripts/capture_cold_restart.py b/backend/scripts/capture_cold_restart.py index f7df96286..cb6c2bf00 100644 --- a/backend/scripts/capture_cold_restart.py +++ b/backend/scripts/capture_cold_restart.py @@ -84,7 +84,7 @@ TERMINAL_EVENTS, TERMINAL_STATUSES, check_fixture, - load_receipt, + receipt_problem, ) from server.api.app import create_app # noqa: E402 from server.common.config import Settings # noqa: E402 @@ -291,7 +291,10 @@ def record(step: str, **fields) -> None: try: client = DeviceClient(server_url) - receipt = load_receipt(receipt_path) + receipt = json.loads(receipt_path.read_text()) + problem = receipt_problem(receipt) + if problem: + raise ValueError(f"{receipt_path}: {problem}") packet_dir = _resolve_packet_dir(receipt, packet_dir) fixture = check_fixture(packet_dir, receipt, errors) record("fixture", packetDir=str(packet_dir), files=len((fixture or {}).get("packet", {}).get("files", []))) @@ -441,7 +444,10 @@ def record(step: str, **fields) -> None: raise RuntimeError(f"no client journal at {journal_path}: the primary leg never ran") journal = json.loads(journal_path.read_text()) client = DeviceClient(server_url) - receipt = load_receipt(receipt_path) + receipt = json.loads(receipt_path.read_text()) + problem = receipt_problem(receipt) + if problem: + raise ValueError(f"{receipt_path}: {problem}") packet_dir = _resolve_packet_dir(receipt, packet_dir) fixture = check_fixture(packet_dir, receipt, errors) record("fixture", packetDir=str(packet_dir), files=len((fixture or {}).get("packet", {}).get("files", []))) @@ -736,10 +742,14 @@ def run_cold_restart( "ok": False, } errors = report["errorCodes"] - receipt = load_receipt(receipt_path) - - # Fail before anything is sent: a pre-replay-field receipt is a caller - # mistake, and the report must name the field instead of guessing. + receipt = json.loads(receipt_path.read_text()) + problem = receipt_problem(receipt) + if problem == "not_a_capture_flow_e2e_receipt": + errors.append(problem) + return _finish(report, errors, f"{receipt_path}: {problem}") + + # Keep the cold-restart report's per-field diagnostics when the shared + # validator rejects a receipt without its replay block. missing = missing_replay_fields(receipt) if missing: report["missingReceiptFields"] = missing