From 3e1e26ee777f8288afc32f14650bb6d7fd7f8b58 Mon Sep 17 00:00:00 2001 From: PhysShell <45852143+PhysShell@users.noreply.github.com> Date: Thu, 1 Oct 2026 19:48:18 +0500 Subject: [PATCH] fix(tests): a repro artifact can be regenerated after its input bytes move The documented two-pass regeneration (reference `--write`, port `OWN_SHADOW_WRITE=1`, reference `--write`) could not start once the input of a committed artifact changed. The reference carried the port's committed capture through verbatim; that capture attests the OLD bytes, so the artifact failed to verify and the writer refused (exit 1). The port's writer in turn refuses until the artifact's bytes equal the file on disk (exit 101). Each side waited for the other, and the only way out was to delete the artifact by hand. The writer no longer carries a foreign capture of other bytes into a rewritten artifact: it is dropped with its reason, exactly like a pre-v3 entry, and the port re-runs and authors its own. Verify mode drops nothing, so a moved input still reads as a stale artifact. The control `carry-moved-input` drives a state no committed fixture can reach (an artifact and its input are always regenerated together). Without the fix it fails three ways: the stale capture is carried, nothing says why, and the rewritten artifact does not verify. Co-Authored-By: Claude Fable 5.1 --- tests/test_repro_fixtures.py | 81 ++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/tests/test_repro_fixtures.py b/tests/test_repro_fixtures.py index 616097ad..f3900895 100644 --- a/tests/test_repro_fixtures.py +++ b/tests/test_repro_fixtures.py @@ -52,10 +52,13 @@ from __future__ import annotations +import contextlib import copy +import io import json import os import sys +import tempfile from typing import Any sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) @@ -231,12 +234,24 @@ def _plan() -> tuple[dict[str, tuple[str, str]], dict[str, dict[str, Any]], return plan, refusal_entries, sorted(artifact_names), problems -def _foreign_engines(golden_path: str) -> list[dict[str, Any]]: +def _foreign_engines(golden_path: str, + rewriting: bytes | None = None) -> list[dict[str, Any]]: """The engine captures already committed in an artifact that this side did NOT author. `--write` reads them back and carries them through, because an engine writes only its own entry: an artifact where one implementation authored another's capture would be a comparison of one thing against - itself.""" + itself. + + `rewriting` is the input the artifact is being REWRITTEN for (`--write` + only). A committed foreign capture attests the bytes that engine read; when + the input has moved since, that capture is of a different document, and + carrying it through yields an artifact that cannot verify — so the writer + used to refuse, the port's writer refuses until the reference has written + (it demands the artifact's bytes equal the file's), and the documented + two-pass order could not start. A capture of other bytes is not carried: + it is dropped with its reason, exactly like a pre-v3 one, and the port + re-runs and authors its own. Verify mode passes nothing and drops nothing, + so a stale artifact still reads as stale.""" if not os.path.exists(golden_path): return [] try: @@ -245,6 +260,16 @@ def _foreign_engines(golden_path: str) -> list[dict[str, Any]]: except (OSError, json.JSONDecodeError): return [] carried, dropped = carry_foreign(committed.get("engines")) + if rewriting is not None: + identity = hash_bytes(rewriting) + fresh = [e for e in carried if e.get("consumed") == identity] + dropped += [ + f"the {e.get('id')!r} entry attests other bytes " + f"({str(e['consumed'].get('digest'))[:12]}… ≠ {identity['digest'][:12]}…): " + f"the input moved since that engine ran, so its capture is of a " + f"different document" + for e in carried if e.get("consumed") != identity] + carried = fresh for reason in dropped: print(f"NOTE: {os.path.basename(golden_path)}: {reason}. Regenerate it " f"with OWN_SHADOW_WRITE=1 cargo test -p own-shadow --test engine.") @@ -1081,6 +1106,55 @@ def _carry_controls() -> list[tuple[str, str]]: "this engine's OWN entry was carried forward instead of " "re-authored — an engine writes only its own entry, and " "carrying its own would replay a stale capture")) + fails += _moved_input_controls() + return fails + + +def _moved_input_controls() -> list[tuple[str, str]]: + """The writer meets a committed artifact whose INPUT has moved. + + No committed fixture can reach this: an artifact and its input are always + regenerated together, so the state only exists between the two halves of a + regeneration — which is exactly when it deadlocked. Driven on a real + artifact projected here, with a foreign entry that attests the old bytes.""" + fails: list[tuple[str, str]] = [] + before = b'{"module": "Moved", "components": []}' + after = b'{"module": "Moved", "components": []}\n' + foreign = {"id": "rust-own-bridge", "consumed": hash_bytes(before), + "derived": {"sarif": {}}, "layers": []} + directory = tempfile.mkdtemp() + path = os.path.join(directory, "moved.repro.json") + try: + with open(path, "w", encoding="utf-8") as f: + json.dump({"engines": [{"id": ENGINE_PYTHON, "layers": []}, foreign]}, f) + with contextlib.redirect_stdout(io.StringIO()) as noted: + unchanged = _foreign_engines(path, rewriting=before) + moved = _foreign_engines(path, rewriting=after) + verifying = _foreign_engines(path) + finally: + os.unlink(path) + os.rmdir(directory) + if unchanged != [foreign]: + fails.append(("carry-moved-input", + "a foreign capture of the SAME bytes was not carried through " + "a rewrite — an unchanged input must keep the port's entry")) + if moved: + fails.append(("carry-moved-input", + "a foreign capture of OTHER bytes was carried into a rewritten " + "artifact — it cannot verify there, so the writer refuses and " + "the port cannot write until the reference has: a regeneration " + "that cannot start")) + if "attests other bytes" not in noted.getvalue(): + fails.append(("carry-moved-input", + "a foreign capture was dropped without saying why")) + if verifying != [foreign]: + fails.append(("carry-moved-input", + "VERIFY dropped a foreign capture — only a rewrite may; in " + "verify a moved input must still read as a stale artifact")) + remaining = verify_repro(project_repro(after, moved)) + if remaining: + fails.append(("carry-moved-input", + f"the rewritten artifact does not verify: {remaining}")) return fails @@ -1532,7 +1606,8 @@ def write() -> int: print(f"wrote {DIGESTS} ({len(plan)} documents)") for case in artifact_names: out = os.path.join(FIXDIR, f"{case}.repro.json") - artifact = project_repro(_read(plan[case][1]), _foreign_engines(out)) + raw = _read(plan[case][1]) + artifact = project_repro(raw, _foreign_engines(out, rewriting=raw)) remaining = verify_repro(artifact) if remaining: print(f"ERROR: {case}: refusing to write an artifact that does not "