Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
78 lines
3.3 KiB
Python
78 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Recover only complete, scope-preserving behavior contracts from valid receipts."""
|
|
import copy
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
from wbv2_autopilot import ROOT, contained, read_json, write_json
|
|
|
|
KINDS = {"positive", "negative", "undo_redo", "save_reopen", "determinism"}
|
|
|
|
|
|
def candidate(worker, contract, root=ROOT):
|
|
try:
|
|
value = read_json(worker)
|
|
proof = read_json(worker.parent / "verified-receipt.json")
|
|
required = set(contract["result"]["sourceUnits"])
|
|
if (proof.get("status") != "verified" or proof.get("job") != contract["job"]
|
|
or proof.get("queueTask") != contract["task"]
|
|
or not required.issubset(value.get("sourceUnits", []))
|
|
or not required.issubset(proof.get("sourceUnits", []))):
|
|
return None
|
|
cases = value.get("cases", [])
|
|
if not KINDS.issubset({case["kind"] for case in cases}):
|
|
return None
|
|
artifacts = {item["path"]: item["sha256"] for item in proof.get("artifacts", [])}
|
|
inputs = proof.get("inputs", {})
|
|
if not artifacts or not inputs:
|
|
return None
|
|
for relative, digest in {**inputs, **artifacts}.items():
|
|
path = contained(root, relative)
|
|
if (not path.is_file() or hashlib.sha256(path.read_bytes()).hexdigest() != digest):
|
|
return None
|
|
for case in cases:
|
|
receipt_path = contained(root, case["receipt"])
|
|
if case["receipt"] not in artifacts:
|
|
return None
|
|
receipt = read_json(receipt_path)
|
|
if (receipt.get("status") != "PASS"
|
|
or receipt.get("runtime") not in {"CHROMIUM_WEBENGINE", "BLENDER_NATIVE"}
|
|
or receipt.get("caseId") != case["id"]
|
|
or receipt.get("kind") != case["kind"]
|
|
or set(receipt.get("unitIds", [])) != set(case["unitIds"])):
|
|
return None
|
|
result = copy.deepcopy(value)
|
|
result.pop("contractKind", None)
|
|
result.pop("mandatoryCacheInputs", None)
|
|
for case in result["cases"]:
|
|
case["receipt"] = "unallocated/" + case["id"] + ".json"
|
|
return result
|
|
except (OSError, ValueError, KeyError, TypeError):
|
|
return None
|
|
|
|
|
|
def main():
|
|
recovered, unavailable = [], []
|
|
for path in sorted((ROOT / "my-tools/local-workflows").glob("*.json")):
|
|
contract = read_json(path)
|
|
if contract.get("result", {}).get("contractKind") != "compilation_only":
|
|
continue
|
|
job = contract["job"]
|
|
candidates = []
|
|
for worker in (ROOT / "tests/golden/WBV2/autopilot/runs").glob(job + "-*/worker-result.json"):
|
|
result = candidate(worker, contract)
|
|
if result:
|
|
candidates.append((worker.stat().st_mtime_ns, result))
|
|
if not candidates:
|
|
unavailable.append(job)
|
|
continue
|
|
contract["result"] = max(candidates, key=lambda item: item[0])[1]
|
|
contract["acceptance"] = "pending fresh execution; historical receipts are not new results"
|
|
write_json(path, contract)
|
|
recovered.append(job)
|
|
print(json.dumps({"recovered": recovered, "count": len(recovered), "unavailable": unavailable}))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|