mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
- scripts/verify_receipt.py: standalone receipt verifier (9 checks: schema, canaries, bundle, row count, claim boundary, per-row fields, hash self-consistency). Works independently of Lean/pipeline. - AGENTS.md: anti-smuggle table now honest — Layers 0/3/4 active, 1/2 noted as manual, 5 partial. No more claims of missing layers. - Emit.lean: #eval witnesses verify emitManifold contains the correct schema string, claim boundary, row count, and bundle validity mark. - Entry gate updated: Layer 1 -> build, Layer 2 -> emission+verify, Layer 4 -> infrastructure (SSH conditional)
116 lines
4.3 KiB
Python
116 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""verify_receipt.py — Standalone receipt verifier.
|
|
|
|
Takes a receipt JSON emitted by rrc-emit-fixture and validates:
|
|
1. Schema is avm_rrc_manifold_v1
|
|
2. AVM canary receipts all pass
|
|
3. Bundle receipt is valid
|
|
4. Row count matches total
|
|
5. All 278 rows are present (if --full)
|
|
6. No row has promotion="promoted" (enforced by claim_boundary)
|
|
|
|
Usage:
|
|
python3 scripts/verify_receipt.py [path_to_receipt.json]
|
|
python3 scripts/verify_receipt.py --full [path_to_receipt.json]
|
|
"""
|
|
|
|
import argparse, json, hashlib, sys
|
|
from pathlib import Path
|
|
|
|
|
|
def verify(data: dict, full: bool = False) -> list[str]:
|
|
errors = []
|
|
|
|
# 1. Schema check
|
|
expected_schema = "avm_rrc_manifold_v1"
|
|
if data.get("schema") != expected_schema:
|
|
errors.append(f"Schema mismatch: got '{data.get('schema')}', expected '{expected_schema}'")
|
|
|
|
# 2. Claim boundary
|
|
expected_boundary = "admissibility-and-routing-pass-only;not-promoted"
|
|
if data.get("claim_boundary") != expected_boundary:
|
|
errors.append(f"Claim boundary mismatch: got '{data.get('claim_boundary')}', expected '{expected_boundary}'")
|
|
|
|
# 3. Canary receipts
|
|
if not data.get("avm_canaries_passed", False):
|
|
errors.append("AVM canaries failed")
|
|
|
|
# 4. Bundle receipt
|
|
if not data.get("bundle_receipt_valid", False):
|
|
errors.append("Bundle receipt invalid")
|
|
|
|
# 5. Summary
|
|
summary = data.get("summary", {})
|
|
total = summary.get("total", 0)
|
|
passed = summary.get("passed_alignment", 0)
|
|
held = summary.get("held", 0)
|
|
|
|
if total != passed + held:
|
|
errors.append(f"Summary mismatch: total={total} != passed={passed} + held={held}")
|
|
|
|
if full and total != 278:
|
|
errors.append(f"Expected 278 rows, got {total}")
|
|
|
|
# 6. Row validation
|
|
rows = data.get("rows", [])
|
|
if len(rows) != total:
|
|
errors.append(f"Row count mismatch: header says {total}, got {len(rows)}")
|
|
|
|
if full and total != len(rows):
|
|
errors.append(f"Incomplete rows: expected {total}, got {len(rows)}")
|
|
|
|
# 7. No promoted rows (claim boundary enforcement)
|
|
promoted = [r for r in rows if r.get("promotion") == "promoted"]
|
|
if promoted:
|
|
errors.append(f"{len(promoted)} rows have promotion='promoted' (violates claim_boundary)")
|
|
|
|
# 8. Each row has required fields
|
|
for i, row in enumerate(rows):
|
|
eid = row.get("equation_id", f"row_{i}")
|
|
if not row.get("equation_id"):
|
|
errors.append(f"Row {i}: missing equation_id")
|
|
if not row.get("shape"):
|
|
errors.append(f"Row {eid}: missing shape")
|
|
if not row.get("alignment_status"):
|
|
errors.append(f"Row {eid}: missing alignment_status")
|
|
|
|
# 9. Self-consistency: compute receipt hash
|
|
receipt_part = {k: v for k, v in data.items() if k != "receipt_hash"}
|
|
canonical = json.dumps(receipt_part, sort_keys=True, separators=(",", ":"))
|
|
computed_hash = hashlib.sha256(canonical.encode()).hexdigest()
|
|
stored_hash = data.get("receipt_hash", "")
|
|
if stored_hash and computed_hash != stored_hash:
|
|
errors.append(f"Receipt hash mismatch: computed {computed_hash[:16]}..., stored {stored_hash[:16]}...")
|
|
|
|
return errors
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Verify an AVM receipt JSON")
|
|
parser.add_argument("path", nargs="?", default="/tmp/rrc_emit_output.json",
|
|
help="Path to receipt JSON")
|
|
parser.add_argument("--full", action="store_true", help="Full verification (check all 278 rows)")
|
|
args = parser.parse_args()
|
|
|
|
data = json.loads(Path(args.path).read_text())
|
|
errors = verify(data, args.full)
|
|
|
|
schema = data.get("schema", "?")
|
|
canaries = data.get("avm_canaries_passed", False)
|
|
bundle = data.get("bundle_receipt_valid", False)
|
|
total = data.get("summary", {}).get("total", 0)
|
|
passed = data.get("summary", {}).get("passed_alignment", 0)
|
|
|
|
print(f"Schema: {schema}")
|
|
print(f"Canaries: {'✅' if canaries else '❌'} {canaries}")
|
|
print(f"Bundle: {'✅' if bundle else '❌'} {bundle}")
|
|
print(f"Rows: {total} total, {passed} passed alignment")
|
|
print(f"Verification: {'✅ PASS' if not errors else '❌ FAIL'}")
|
|
for e in errors:
|
|
print(f" - {e}")
|
|
|
|
return 0 if not errors else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|