78 lines
4.0 KiB
Python
78 lines
4.0 KiB
Python
"""Read-only verification of the archived default-preservation baseline evidence."""
|
|
from pathlib import Path
|
|
import hashlib
|
|
import json
|
|
import zipfile
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
|
|
|
|
def digest(data):
|
|
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
|
|
|
|
|
def read_json(path):
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
|
|
def require(condition, message):
|
|
if not condition:
|
|
raise SystemExit(message)
|
|
|
|
|
|
manifest = read_json(ROOT / "files.json")
|
|
actual_paths = {
|
|
str(p.relative_to(ROOT)).replace("\\", "/")
|
|
for p in ROOT.rglob("*") if p.is_file()
|
|
and p.name != "files.json" and "__pycache__" not in p.parts
|
|
}
|
|
require(actual_paths == set(manifest), "Archive membership mismatch")
|
|
for path, expected in manifest.items():
|
|
require(digest((ROOT / path).read_bytes()) == expected, f"File mismatch: {path}")
|
|
|
|
captured_inputs = []
|
|
for name, passed, failed in (("baseline-1", 7, 4), ("baseline-2", 8, 3)):
|
|
gate = ROOT / name
|
|
result = read_json(gate / "result.json")
|
|
started = read_json(gate / "started.json")
|
|
require(all(result[key] == value for key, value in started.items()), f"{name}: start/result mismatch")
|
|
require(result["exitCode"] == 101 and result["inputsUnchanged"], f"{name}: unexpected exit/input change")
|
|
for field, filename in (("inputs", "inputs.json"), ("archive", "inputs.zip"), ("stdout", "stdout.log"), ("stderr", "stderr.log")):
|
|
require(digest((gate / filename).read_bytes()) == result[field], f"{name}: {field} mismatch")
|
|
require(digest((ROOT / "gate.py").read_bytes()) == result["helper"], f"{name}: runner mismatch")
|
|
inputs = read_json(gate / "inputs.json")
|
|
captured_inputs.append(inputs)
|
|
require(len(inputs) == result["inputCount"] == 605, f"{name}: input count")
|
|
with zipfile.ZipFile(gate / "inputs.zip") as archive:
|
|
require(len(archive.namelist()) == len(inputs) and set(archive.namelist()) == set(inputs), f"{name}: ZIP membership")
|
|
for path, expected in inputs.items():
|
|
require(digest(archive.read(path)) == expected, f"{name}: input mismatch: {path}")
|
|
stdout = (gate / "stdout.log").read_text(encoding="utf-8")
|
|
require(f"test result: FAILED. {passed} passed; {failed} failed; 0 ignored;" in stdout, f"{name}: result counts")
|
|
test_lines = [line for line in stdout.splitlines() if line.startswith("test ") and " ... " in line]
|
|
require(len(test_lines) == 11, f"{name}: named outcome count")
|
|
failures = {line.split()[1] for line in test_lines if line.endswith(" ... FAILED")}
|
|
expected_failures = {
|
|
"explicit_exchange_operation_none_survives_exchange_binding_edit",
|
|
"explicit_node_empty_port_anchors_survive_position_edit",
|
|
"explicit_trace_semantic_none_survives_snapshot_prose_edit",
|
|
}
|
|
if name == "baseline-1":
|
|
expected_failures.add("relation_kind_change_updates_explicit_value_when_context_default_changes")
|
|
require(failures == expected_failures, f"{name}: unexpected failing tests")
|
|
cargo_artifacts = []
|
|
for line in stdout.splitlines():
|
|
try:
|
|
item = json.loads(line)
|
|
except json.JSONDecodeError:
|
|
continue
|
|
if isinstance(item, dict) and item.get("reason") == "compiler-artifact" and item.get("executable"):
|
|
cargo_artifacts.append(item)
|
|
require(cargo_artifacts == [entry["cargo"] for entry in result["artifacts"]], f"{name}: Cargo artifact mismatch")
|
|
require(len(cargo_artifacts) == 1 and result["artifacts"][0]["readback"]["bytes"] > 0, f"{name}: executable receipt missing")
|
|
|
|
changed = {p for p in captured_inputs[0].keys() | captured_inputs[1].keys() if captured_inputs[0].get(p) != captured_inputs[1].get(p)}
|
|
require(changed == {"crates/oa-core/tests/default_omission_preservation.rs"}, "Baseline production inputs differ")
|
|
print(f"Verified {len(manifest)} pinned files and two 605-input Windows baselines (expected failures retained).")
|
|
print("Executable receipts are historical hashes; binaries are not included. This verifies evidence integrity, not a production fix.")
|