Research-Stack/4-Infrastructure/shim/lean_proof_replay_receipt.py
2026-05-11 22:18:31 -05:00

154 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""Receipt generator for the LeanDojo/mathlib proof-boundary lane.
LeanDojo/mathlib are route priors only. This receipt promotes only local Lean
replay evidence: targeted `lake build` plus witness `#eval` output from a tiny
extension theorem fixture.
"""
from __future__ import annotations
import hashlib
import json
import subprocess
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
REPO = Path(__file__).resolve().parents[2]
LEAN_ROOT = REPO / "0-Core-Formalism" / "lean" / "Semantics"
LEAN_FILE = LEAN_ROOT / "ExtensionScaffold" / "Compression" / "ProofReplay.lean"
OUT_DIR = REPO / "shared-data" / "data" / "lean_proof_replay"
RECEIPT = OUT_DIR / "lean_proof_replay_receipt.json"
SUMMARY = OUT_DIR / "lean_proof_replay_receipt.md"
EXPECTED_WITNESSES = ["94", "true", "true", "false", "false"]
def stable_json(obj: Any) -> str:
return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
def sha256_text(text: str) -> str:
return hashlib.sha256(text.encode("utf-8", errors="replace")).hexdigest()
def sha256_file(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def rel(path: Path) -> str:
return str(path.relative_to(REPO))
def run_command(argv: list[str], cwd: Path) -> dict[str, Any]:
proc = subprocess.run(argv, cwd=cwd, text=True, capture_output=True, check=False)
return {
"argv": argv,
"cwd": rel(cwd),
"returncode": proc.returncode,
"stdout": proc.stdout,
"stderr": proc.stderr,
"stdout_hash": sha256_text(proc.stdout),
"stderr_hash": sha256_text(proc.stderr),
}
def parse_witnesses(stdout: str) -> list[str]:
witnesses: list[str] = []
for line in stdout.splitlines():
stripped = line.strip()
if stripped in {"true", "false"} or stripped.isdigit():
witnesses.append(stripped)
continue
if ": " in stripped:
tail = stripped.rsplit(": ", 1)[-1].strip()
if tail in {"true", "false"} or tail.isdigit():
witnesses.append(tail)
return witnesses
def write_summary(receipt: dict[str, Any], path: Path) -> None:
lines = [
"# Lean Proof Replay Receipt",
"",
f"Schema: `{receipt['schema']}` ",
f"Decision: `{receipt['decision']}` ",
f"Status: `{receipt['status']}` ",
f"Receipt hash: `{receipt['receipt_hash']}`",
"",
receipt["claim_boundary"],
"",
"## Witnesses",
"",
f"Expected: `{receipt['expected_witnesses']}`",
"",
f"Observed: `{receipt['observed_witnesses']}`",
"",
"## Commands",
"",
]
for command in receipt["commands"]:
lines.extend(
[
f"- `{' '.join(command['argv'])}`",
f" - returncode: `{command['returncode']}`",
f" - stdout hash: `{command['stdout_hash']}`",
f" - stderr hash: `{command['stderr_hash']}`",
]
)
lines.append("")
path.write_text("\n".join(lines), encoding="utf-8")
def main() -> int:
OUT_DIR.mkdir(parents=True, exist_ok=True)
build = run_command(["lake", "build", "ExtensionScaffold.Compression.ProofReplay"], LEAN_ROOT)
eval_run = run_command(["lake", "env", "lean", "ExtensionScaffold/Compression/ProofReplay.lean"], LEAN_ROOT)
observed = parse_witnesses(eval_run["stdout"])
build_pass = build["returncode"] == 0
witness_pass = observed == EXPECTED_WITNESSES
status = "ADMIT_FIXTURE" if build_pass and witness_pass else "HOLD_DIAGNOSTIC"
receipt = {
"schema": "lean_proof_replay_receipt_v1",
"generated_at_utc": datetime.now(timezone.utc).isoformat(),
"lean_file": rel(LEAN_FILE),
"lean_file_hash": sha256_file(LEAN_FILE),
"module": "ExtensionScaffold.Compression.ProofReplay",
"commands": [build, eval_run],
"expected_witnesses": EXPECTED_WITNESSES,
"observed_witnesses": observed,
"build_pass": build_pass,
"witness_pass": witness_pass,
"status": status,
"decision": "HOLD",
"claim_boundary": (
"LeanDojo/mathlib proof-boundary fixture only. External proof corpora "
"may propose obligations, but promotion requires local Lean replay. "
"This receipt proves only a tiny local admission predicate fixture, "
"not any external theorem, compression benchmark, or mathlib coverage claim."
),
}
receipt["receipt_hash"] = sha256_text(stable_json({k: v for k, v in receipt.items() if k != "receipt_hash"}))
RECEIPT.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8")
write_summary(receipt, SUMMARY)
print(
json.dumps(
{
"receipt": rel(RECEIPT),
"summary": rel(SUMMARY),
"receipt_hash": receipt["receipt_hash"],
"status": receipt["status"],
"observed_witnesses": observed,
},
indent=2,
sort_keys=True,
)
)
return 0 if build_pass and witness_pass else 1
if __name__ == "__main__":
raise SystemExit(main())