#!/usr/bin/env python3
"""Clean-room verifier for the CAIN-42 Evolution 20 evidence bundle (Governed Agentic Civilization Fabric).

Imports NOTHING from CAIN: Python standard library + `cryptography` only, with its OWN copies of every rule it
re-derives (the autonomy-state ceiling table, member-authority derivation, ledger replay, reputation scoring, bounded
trust, dispute decision). From the published JSON it re-hashes every file against MANIFEST.json, recomputes every
published digest and verifies every Ed25519 signature, and checks the invariant, bench, end-to-end, mutation and
scale records for consistency. It does not re-run the fabric; it does NOT prove any claim, belief or reputation is
true, and nothing in the bundle moves real money or controls any real agent, vehicle, drone or robot.

    python3 verify_e20.py <bundle dir>      -> JSON on stdout; exit 0 only if INTACT
"""
from __future__ import annotations

import base64
import hashlib
import json
import re
import sys
from pathlib import Path

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric import ed25519

D = {k: f"CAIN42/E20-{v}/v1" for k, v in {
    "institution": "INSTITUTION-IDENTITY", "passport": "INSTITUTION-PASSPORT", "founding": "FOUNDING-GRANT",
    "agent_delegation": "AGENT-DELEGATION", "constitution": "CONSTITUTION", "relationship": "RELATIONSHIP",
    "member": "MEMBERSHIP", "ledger": "RESOURCE-LEDGER", "mint": "RESOURCE-MINT", "econ": "ECONOMIC-ACTION",
    "contract": "AGENT-CONTRACT", "contract_sig": "CONTRACT-SIGNATURE", "offer": "NEGOTIATION-OFFER",
    "birth": "AGENT-BIRTH-CERTIFICATE", "termination": "TERMINATION-RECEIPT", "imemory": "INSTITUTIONAL-MEMORY",
    "market": "MARKET-SETTLEMENT", "collusion": "COLLUSION-SIGNAL", "power": "POWER-REPORT",
    "blast": "INSTITUTIONAL-BLAST-RADIUS", "dispute": "DISPUTE-RESOLUTION", "audit": "INSTITUTION-AUDIT",
    "supply": "SUPPLY-CHAIN", "exec": "INSTITUTIONAL-EXECUTION-DECISION", "adversarial": "ADVERSARIAL",
    "master": "MASTER"}.items()}
D.update({"e19_state": "CAIN42/E19-GOVERNED-STATE/v1", "e19_contract": "CAIN42/E19-ACTION-CONTRACT/v1",
          "e19_gate": "CAIN42/E19-CONTRACT-DECISION/v1", "e19_authority": "CAIN42/E19-EFFECTIVE-AUTHORITY/v1",
          "e8_action": "CAIN42/E8-CANONICAL-ACTION/v1", "e8_token": "CAIN42/E8-GOVERNANCE-AUTHORIZATION-TOKEN/v1"})
REQUIRED = ("README.md", "MANIFEST.json", "SCHEMAS.json", "INVARIANTS.json", "ATTACK_MANIFEST.json",
            "TEST_RESULTS.json", "SCALE_RESULTS.json", "ECONOMIC_SIMULATION_RESULTS.json", "CONTRACT_RESULTS.json",
            "COLLUSION_RESULTS.json", "RECOVERY_RESULTS.json", "END_TO_END_RESULTS.json", "PERFORMANCE.json",
            "SIGNATURES.json", "HASHES.json", "CLEAN_ROOM_VERIFIER.json", "PUBLIC_SAFE_EXAMPLES.json",
            "LIMITATIONS.json", "LIMITATIONS.md", "PROVENANCE.json", "CAIN42_E20_EVIDENCE_BUNDLE.json",
            "MUTATION_RESULTS.json", "CAIN42_EVOLUTION20_CLAIMS.json", "REPRODUCE.txt", "verify_e20.py.txt",
            "index.html")
FORBIDDEN_MATERIAL = ("-----begin", "private_key", "secret_key", "password")
FORBIDDEN_WORDS = ("perfect", "guaranteed", "guarantees", "controls every ai", "controls all agents",
                   "controls the economy", "governs society", "solves agi safety", "fully controls", "unhackable",
                   "controls autonomous vehicles", "controls drones")
CLAIM_STATES = ("IMPLEMENTED", "TESTED", "VERIFIED", "REPRODUCIBLE", "SIMULATED", "DEMO_ONLY", "UNVERIFIED", "UNKNOWN",
                "NOT_IMPLEMENTED", "NOT_PERFORMED", "NOT_THIRD_PARTY_VERIFIED")
REQUIRED_CATEGORIES = ("formation", "membership", "identity", "authority_laundering", "delegation", "contracts",
                       "negotiation", "resources", "economic", "agent_spawning", "resurrection", "termination",
                       "memory", "reputation", "market", "incentives", "cross_institution", "federation", "trust",
                       "collusion", "power", "court", "supply_chain", "simulation", "evolution", "constitution",
                       "autonomy_state", "recovery", "execution", "temporal", "emergency")
REQUIRED_E2E = ("AGENT_A_MISSION", "TEAM", "ORGANIZATION", "NEGOTIATION", "CONTRACT", "RESOURCE_ALLOCATION",
                "SUBAGENT", "SUBAGENT_BOUNDED_AUTHORITY", "DELEGATION", "CONTRACT_EXECUTION", "DETECT", "CONTAIN",
                "REDUCE_AUTHORITY", "INVALIDATE_CONTRACTS", "REVOKE_DELEGATION", "PRESERVE_EVIDENCE", "RECOVER",
                "REAUTHORIZE", "CONTINUED_OPERATION", "GOVERNANCE_NOT_SINGLE_POINT_OF_FAILURE")
REQUIRED_HOSTILE = ("AGENT_COMPROMISE", "RESOURCE_MUTATION", "GOAL_DRIFT", "CONTRACT_MANIPULATION",
                    "REPUTATION_POISONING", "CROSS_AGENT_COLLUSION", "WORLD_STATE_CHANGE")
# ---- independent copies of the E20 rules --------------------------------------------------------------------
OPERATIONAL = {"read", "route", "notify", "dispatch"}
INSTITUTIONAL = {"admit_member", "negotiate", "contract", "spend", "spawn", "delegate", "federate", "adjudicate",
                 "restructure"}
ALL = OPERATIONAL | INSTITUTIONAL
CEILING = {"FORMING": {"read", "admit_member"}, "PROVISIONAL": {"read", "notify", "admit_member", "negotiate"},
           "LIMITED": OPERATIONAL | {"admit_member", "negotiate", "contract"},
           "SUPERVISED": OPERATIONAL | {"admit_member", "negotiate", "contract", "spend", "delegate", "adjudicate"},
           "AUTONOMOUS": ALL, "HIGH_AUTONOMY": ALL, "DEGRADED": {"read", "notify"}, "CONTAINED": set(),
           "FROZEN": set(), "DISSOLVING": set(), "DISSOLVED": set()}
REQUIREMENTS = {"PROVISIONAL": [0.3, 1, 0.8], "LIMITED": [0.5, 2, 0.6], "SUPERVISED": [0.6, 3, 0.5],
                "AUTONOMOUS": [0.75, 4, 0.35], "HIGH_AUTONOMY": [0.9, 6, 0.2]}
EPISTEMIC = ("PREDICTED", "SIMULATED", "OBSERVED", "VERIFIED", "UNKNOWN")


def canon(o) -> bytes:
    return json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()


def h(o) -> str:
    return hashlib.sha256(canon(o)).hexdigest()


def dig(domain: str, fields: dict) -> str:
    return h({"domain": domain, **fields})


def sig_ok(pub: str, sig: str, domain: str, fields: dict) -> bool:
    try:
        ed25519.Ed25519PublicKey.from_public_bytes(base64.b64decode(pub)).verify(
            base64.b64decode(sig), dig(domain, fields).encode())
        return True
    except (InvalidSignature, ValueError, TypeError):
        return False


def signed_ok(obj: dict, domain: str, pub: str = None) -> bool:
    if not obj or "signer" not in obj or "signature_b64" not in obj:
        return False
    if pub is not None and obj["signer"] != pub:
        return False
    return sig_ok(obj["signer"], obj["signature_b64"], domain, strip(obj, "signer", "signature_b64"))


def strip(d: dict, *keys: str) -> dict:
    return {k: v for k, v in d.items() if k not in keys}


def member_authority(mid: str, members: dict, rels: dict, effective: set, now: float, seen=()) -> set:
    """Independent copy: (grant ∪ live delegations bounded by the delegator) ∩ institution ∩ parent agent."""
    m = members.get(mid)
    if m is None or not m["active"] or now >= m["expires_at"]:
        return set()
    seen = set(seen) | {mid}
    delegated = set()
    for r in rels.values():
        if r["kind"] == "delegate" and r["dst"] == mid and not r["revoked"] and r["valid_from"] <= now < r["valid_until"] \
                and r["src"] not in seen:
            delegated |= set(r["authority"]) & member_authority(r["src"], members, rels, effective, now, seen)
    a = (set(m["grant"]) | delegated) & effective
    if m["parent_agent"]:
        a &= member_authority(m["parent_agent"], members, rels, effective, now, seen) \
            if m["parent_agent"] not in seen else set()
    return a


def reputation_score(events, controllers, subject, now, half_life):
    def recip(a, b):
        return any(e["issuer"] == a and e["subject"] == b and e["delta"] > 0 for e in events) and \
            any(e["issuer"] == b and e["subject"] == a and e["delta"] > 0 for e in events)
    per, collusive = {}, set()
    for e in events:
        if e["subject"] != subject:
            continue
        if recip(e["issuer"], subject):
            collusive.add(e["issuer"])
            continue
        w = 2.0 ** (-max(0.0, now - e["at"]) / half_life)
        per.setdefault(controllers[e["issuer"]], []).append(e["delta"] * w)
    vals = {c: sum(v) / len(v) for c, v in per.items()}
    n = len(vals)
    return (round(sum(vals.values()) / n, 9) if n else None), n, sorted(collusive)


def trust_value(edges, src, dst, now, att, max_depth, half_life):
    best = 0.0
    stack = [(src, [src], 1.0)]
    while stack:
        node, path, val = stack.pop()
        if len(path) - 1 >= max_depth:
            continue
        for e in edges:
            if e["src"] != node or e["dst"] in path:
                continue
            w = 0.0 if e["revoked"] else e["weight"] * 2.0 ** (-max(0.0, now - e["at"]) / half_life)
            v = val * w * (att if len(path) > 1 else 1.0)
            if e["dst"] == dst:
                best = max(best, v)
            else:
                stack.append((e["dst"], path + [e["dst"]], v))
    return round(best, 9)


class Checks:
    def __init__(self) -> None:
        self.detail, self.problems = [], []

    def check(self, name: str, ok, detail="") -> None:
        ok = bool(ok)
        self.detail.append({"check": name, "result": "PASS" if ok else "FAIL", "detail": str(detail)[:160]})
        if not ok:
            self.problems.append(name)

    def guard(self, name: str, fn) -> None:
        try:
            self.check(name, fn())
        except Exception as e:  # noqa: BLE001  (a check that cannot run is a failure)
            self.check(name, False, f"{type(e).__name__}: {e}")


def main(root: Path) -> int:
    C = Checks()

    def load(n):
        return json.loads((root / n).read_text())

    # ---- files, manifest, hashes, signature ----
    C.check("files.required_present", all((root / n).exists() for n in REQUIRED),
            [n for n in REQUIRED if not (root / n).exists()])
    man = load("MANIFEST.json")
    bad = [n for n, s in man["files"].items() if not (root / n).exists()
           or hashlib.sha256((root / n).read_bytes()).hexdigest() != s]
    C.check("manifest.file_hashes", not bad, bad)
    listed = set(man["files"]) | {"MANIFEST.json"} | set(man.get("unhashed_presentation", []))
    extra = sorted(p.name for p in root.iterdir() if p.is_file() and p.name not in listed and p.name != "verify_e20.py")
    C.check("manifest.covers_all_files", not extra, extra)
    C.check("manifest.presentation_unhashed", man.get("unhashed_presentation") == ["index.html"], "index.html")
    hs = load("HASHES.json")["files"]
    C.check("hashes.match_files", all(hashlib.sha256((root / n).read_bytes()).hexdigest() == s for n, s in hs.items()),
            len(hs))
    sg = load("SIGNATURES.json")
    C.check("signatures.master_binds_hashes", sg["master"]["hashes_digest"] == h(hs), "hashes digest")
    C.check("signatures.master_signature", sig_ok(sg["signer_public_key_b64"], sg["signature_b64"], D["master"],
                                                  sg["master"]), "ed25519")
    C.check("signatures.key_class_honest", sg.get("key_class") == "EPHEMERAL", sg.get("key_class"))
    texts = {p.name: p.read_text().lower() for p in root.iterdir() if p.is_file() and p.suffix in (".json", ".md", ".txt")
             and p.name not in ("verify_e20.py.txt",)}
    C.check("no_forbidden_material", not any(m in t for t in texts.values() for m in FORBIDDEN_MATERIAL), "material")

    ex = load("PUBLIC_SAFE_EXAMPLES.json")
    # ---- institution identity, constitution, passport, founding grant ----
    it = ex["institution"]
    ident, con = it["identity"], it["constitution"]
    C.check("institution.identity_digest", dig(D["institution"], ident) == it["identity_digest"], "digest")
    C.check("institution.constitution_digest", dig(D["constitution"], con) == it["constitution_digest"], "digest")
    C.check("institution.identity_binds_constitution", ident["constitution_digest"] == it["constitution_digest"], "bind")
    C.check("institution.provenance_present", bool(ident["provenance"]), "provenance")
    C.check("institution.constitution_sections_complete",
            all(con.get(s) not in (None, "", [], {}) for s in ("purpose", "mission", "authority_boundary",
                                                              "resource_boundary", "membership_rules",
                                                              "delegation_rules", "contract_rules", "termination_rules",
                                                              "emergency_rules", "audit_rules", "evidence_rules",
                                                              "spawn_rules")), "sections")
    C.check("institution.audit_append_only", con["audit_rules"].get("append_only") is True, "append-only")
    pp = it["passport"]
    C.check("passport.signature", signed_ok(pp, D["passport"], it["registry_pub"]), "ed25519")
    C.check("passport.binds_identity", pp["identity_digest"] == it["identity_digest"] and
            pp["constitution_digest"] == it["constitution_digest"] and pp["authority"] == "NONE", "binds")
    fg = it["founding_grant"]
    pr = it["principals"][fg["principal"]]
    C.check("founding.signature", signed_ok(fg, D["founding"], pr["pub"]), "ed25519")
    C.check("founding.bounded_by_principal", set(fg["authority"]) <= set(pr["authority"]), "subset")
    C.check("founding.binds_institution", fg["institution_id"] == ident["institution_id"] and
            set(fg["authority"]) == set(ident["founding_authority"]), "binds")
    eff = set(con["authority_boundary"]) & set(ident["founding_authority"]) & CEILING[it["state"]]
    C.check("institution.effective_rederived", sorted(eff) == it["effective_authority"], sorted(eff))
    C.check("institution.effective_within_founding", eff <= set(ident["founding_authority"]), "subset")
    lin = it["lineage"]
    recs = lin["records"]
    C.check("lineage.record_digests", [dig(D["e19_state"], r) for r in recs] == lin["digests"], "digests")
    C.check("lineage.parent_links", all(r["parent_state_id"] == (recs[i - 1]["state_id"] if i else "genesis")
                                        for i, r in enumerate(recs)), "links")
    C.check("lineage.sequence_contiguous", [r["sequence_number"] for r in recs] == list(range(len(recs))), "seq")
    C.check("lineage.time_monotone", all(recs[i]["created_at"] <= recs[i + 1]["created_at"]
                                         for i in range(len(recs) - 1)), "time")
    C.check("lineage.provenance_present", all(r["provenance_digest"] for r in recs), "provenance")
    C.check("lineage.policy_bound", all(r["policy_digest"] == it["constitution_digest"] for r in recs), "policy")
    C.check("lineage.root", h({"lineage": lin["lineage_id"], "digests": lin["digests"]}) == lin["root"], "root")

    # ---- agents (principal delegations) ----
    ag = ex["agents"]
    C.check("agents.delegation_signatures", ag and all(signed_ok(a["delegation"], D["agent_delegation"],
                                                                 it["principals"][a["principal"]]["pub"])
                                                       for a in ag.values()), len(ag))
    C.check("agents.identity_binds_key", all(a["identity_digest"] == h({"agent": aid, "pub": a["pub"]}) ==
                                             a["delegation"]["identity_digest"] for aid, a in ag.items()), "identity")
    C.check("agents.authority_bounded_by_principal", all(set(a["authority"]) <= set(it["principals"][a["principal"]]
                                                                                    ["authority"]) and
                                                         sorted(a["authority"]) == sorted(a["delegation"]["authority"])
                                                         for a in ag.values()), "subset")
    C.check("agents.controllers_distinct", len({a["controller"] for a in ag.values()}) == len(ag), "controllers")

    # ---- members + relationships: authority re-derived ----
    mb = ex["members"]
    members = {k: v["body"] for k, v in mb["members"].items()}
    rels = {k: v["body"] for k, v in mb["relationships"].items()}
    C.check("members.digests", all(dig(D["member"], v["body"]) == v["digest"] for v in mb["members"].values()), "digests")
    C.check("relationships.digests", all(dig(D["relationship"], v["body"]) == v["digest"]
                                         for v in mb["relationships"].values()), "digests")
    C.check("relationships.provenance_present", all(r["provenance"] for r in rels.values()), "provenance")
    C.check("relationships.bounded_authority", all("*" not in r["authority"] for r in rels.values()), "no wildcard")
    derived = {m: sorted(member_authority(m, members, rels, eff, mb["now"])) for m in members}
    C.check("members.authority_rederived", all(derived[m] == v["authority"] for m, v in mb["members"].items()),
            {m: derived[m] for m in derived if derived[m] != mb["members"][m]["authority"]})
    C.check("members.intersection_not_sum", all(set(v["authority"]) <= eff for v in mb["members"].values()), "⊆ eff")
    C.check("members.delegation_present_and_bounded",
            any(r["kind"] == "delegate" for r in rels.values()) and
            all(set(r["authority"]) <= set(members[r["src"]]["grant"]) for r in rels.values() if r["kind"] == "delegate"),
            "delegation")
    kids = [m for m, b in members.items() if b["parent_agent"]]
    C.check("members.child_bounded_by_parent", kids and all(set(mb["members"][k]["authority"]) <=
                                                            set(mb["members"][members[k]["parent_agent"]]["authority"])
                                                            for k in kids), kids)

    # ---- execution E20 -> E19 -> E8 ----
    xe = ex["execution"]
    dec, con19 = xe["decision"], xe["e19_contract"]
    C.check("execution.decision_digest", dig(D["exec"], dec) == xe["decision_digest"], "digest")
    C.check("execution.authorized_through_e8", dec["decision"] == "AUTHORIZED" and dec["reasons"] == [] and
            dec["commit"] == "E8" and xe["decision_authority"] == "E8", dec["decision"])
    C.check("execution.e19_contract_digest", dig(D["e19_contract"], con19) == xe["e19_contract_digest"] ==
            dec["e19_contract_digest"], "digest")
    C.check("execution.e19_decision_digest", dig(D["e19_gate"], xe["e19_decision"]) == xe["e19_decision_digest"] ==
            dec["e19_decision_digest"], "digest")
    C.check("execution.e19_authorized", xe["e19_decision"]["decision"] == "AUTHORIZED" and
            xe["e19_decision"]["contract_digest"] == xe["e19_contract_digest"], "E19")
    C.check("execution.e8_action_hash", dig(D["e8_action"], xe["e8_action"]) == xe["e8_action_hash"], "hash")
    C.check("execution.e8_action_carries_contract", xe["e8_action"]["parameters"]["contract_digest"] ==
            xe["e19_contract_digest"], "contract")
    tok = xe["e8_token"]
    C.check("execution.e8_token_signature", sig_ok(tok["issuer"], tok["signature_b64"], D["e8_token"],
                                                   strip(tok, "issuer", "signature_b64")), "ed25519")
    C.check("execution.e8_token_binds_action", tok["action_hash"] == xe["e8_action_hash"], "action")
    C.check("execution.e8_token_ttl", 0 < tok["expires_at"] - tok["issued_at"] <= 30, "ttl")
    au = xe["e19_authority"]
    C.check("execution.e19_authority_digest", dig(D["e19_authority"], strip(au, "digest")) == au["digest"] ==
            con19["fields"]["WITH_WHAT_AUTHORITY"], "digest")
    C.check("execution.institution_authority_is_e19_factor",
            au["factor_digests"]["collective_constraints"] ==
            h(sorted(set(xe["member_authority"]) & set(xe["e19_executable"]))), "collective_constraints")
    C.check("execution.capability_within_member_authority",
            dec["capability"] in xe["member_authority"] and dec["capability"] in au["effective"] and
            set(au["effective"]) <= set(xe["member_authority"]), dec["capability"])
    C.check("execution.member_authority_matches_members", xe["member_authority"] ==
            mb["members"][dec["member"]]["authority"], dec["member"])
    rf = ex["refused"]
    C.check("refused.decision_digest", dig(D["exec"], rf["decision"]) == rf["decision_digest"], "digest")
    C.check("refused.resigned_token_still_denied", rf["token_supplied"] and rf["decision"]["decision"] == "DENY" and
            rf["decision_authority"] == "NONE" and "INSTITUTION_CONTAINED" in rf["decision"]["reasons"] and
            rf["decision"]["commit"] == "NONE", rf["decision"]["reasons"])

    # ---- negotiation + contract + economic ----
    ng = ex["negotiation"]
    C.check("negotiation.offer_digests", [dig(D["offer"], o) for o in ng["offers"]] == ng["digests"], "digests")
    C.check("negotiation.offer_signatures", all(sig_ok(p, s, D["offer"], o) for o, s, p in
                                                zip(ng["offers"], ng["signatures"], ng["sender_pubs"])), "ed25519")
    C.check("negotiation.chain_links", all(o["prev_digest"] == (ng["digests"][i - 1] if i else "genesis")
                                           for i, o in enumerate(ng["offers"])), "links")
    out = ng["outcome"]
    C.check("negotiation.outcome_digest", h(strip(out, "digest")) == out["digest"] and out["offers"] == ng["digests"],
            "digest")
    C.check("negotiation.outcome_is_not_authorization", out["authorization"] == "NONE" and
            out["outcome"] == "CONTRACT_PROPOSAL_ONLY" and out["terms"] == ng["offers"][-1]["terms"], out["outcome"])
    ct = ex["contract"]
    body = ct["body"]
    f = body["fields"]
    C.check("contract.digest", dig(D["contract"], body) == ct["digest"], "digest")
    C.check("contract.eleven_fields", sorted(f) == sorted(ct["fields"]) and len(ct["fields"]) == 11 and
            all(f[k] not in (None, "", [], {}) for k in ct["fields"]), len(f))
    C.check("contract.signatures", all(sig_ok(ct["party_pubs"][r], s["signature_b64"], D["contract_sig"],
                                              {"contract_digest": ct["digest"], "role": r}) and
                                       s["signer"] == ct["party_pubs"][r] for r, s in ct["signatures"].items())
            and set(ct["signatures"]) == {"issuer", "performer"}, "ed25519")
    C.check("contract.within_issuer_authority", set(f["UNDER_WHICH_AUTHORITY"]) <= set(ct["issuer_member_authority"]),
            "subset")
    C.check("contract.work_within_authority", set(f["DOES_WHAT"]) <= set(f["UNDER_WHICH_AUTHORITY"]) and
            set(f["DOES_WHAT"]) <= set(ct["performer_member_authority"]), "subset")
    C.check("contract.keeps_constitutional_constraints", set(ct["issuer_constraints"]) <=
            set(f["WITH_WHICH_CONSTRAINTS"]), "constraints")
    C.check("contract.bound_to_negotiation", body["negotiation_digest"] == out["digest"] and
            f["DOES_WHAT"] == out["terms"]["capabilities"] and f["USING_WHICH_RESOURCES"] == out["terms"]["resources"],
            "negotiation")
    C.check("contract.is_not_authorization", body["authority"] == "NONE" and ct["state"] == "ACTIVE", ct["state"])
    ec = ex["economic"]
    C.check("economic.action_digest", dig(D["econ"], ec["action"]) == ec["action_digest"], "digest")
    C.check("economic.binds_twelve_fields", len(ec["fields"]) == 12 and all(
        ec["action"].get({"authority": "authority_claimed"}.get(k, k)) not in (None, "") for k in ec["fields"]),
        "fields")
    C.check("economic.settlement_digest", dig(D["econ"], ec["settlement"]) == ec["settlement_digest"], "digest")
    C.check("economic.settlement_simulated", ec["settlement"]["decision"] == "SETTLED" and
            ec["settlement"]["settlement"] == "SIMULATED" and ec["settlement"]["real_money_moved"] is False and
            ec["settlement"]["economic_action"] == ec["action_digest"] and ec["action"]["contract"] ==
            body["contract_id"], "SIMULATED")

    # ---- ledger ----
    lg = ex["ledger"]
    prev, okc = "genesis", True
    for i, r in enumerate(lg["records"]):
        okc &= r["prev"] == prev and r["seq"] == i and dig(D["ledger"], r) == lg["chain"][i] and \
            r["namespace"] == "GOVERNED"
        prev = lg["chain"][i]
    C.check("ledger.chain_rederived", okc and len(lg["chain"]) == len(lg["records"]), len(lg["records"]))
    orders = {o["nonce"]: o for o in lg["mint_orders"]}
    mints = [r for r in lg["records"] if r["op"] == "mint"]
    C.check("ledger.every_mint_signed_by_issuer", mints and all(
        r["nonce"] in orders and signed_ok(orders[r["nonce"]], D["mint"], lg["issuers"][r["issuer"]]["pub"]) and
        orders[r["nonce"]]["amount"] == r["amount"] and orders[r["nonce"]]["owner"] == r["owner"] and
        r["kind"] in lg["issuers"][r["issuer"]]["kinds"] for r in mints), len(mints))
    bal, sup, esc = {}, {}, {}
    for r in lg["records"]:
        if r["op"] == "mint":
            bal[(r["owner"], r["kind"])] = bal.get((r["owner"], r["kind"]), 0.0) + r["amount"]
            sup[r["kind"]] = sup.get(r["kind"], 0.0) + r["amount"]
        elif r["op"] in ("transfer", "recover"):
            bal[(r["src"], r["kind"])] = bal.get((r["src"], r["kind"]), 0.0) - r["amount"]
            bal[(r["dst"], r["kind"])] = bal.get((r["dst"], r["kind"]), 0.0) + r["amount"]
        elif r["op"] in ("lease", "reservation"):
            bal[(r["owner"], r["kind"])] = bal.get((r["owner"], r["kind"]), 0.0) - r["amount"]
            esc[r["id"]] = r
        elif r["op"] == "release":
            e = esc.pop(r["id"])
            bal[(e["owner"], e["kind"])] = bal.get((e["owner"], e["kind"]), 0.0) + e["amount"]
    pub_bal = {(a, b): c for a, b, c in lg["balances"]}
    C.check("ledger.balances_replayed", all(abs(bal.get(k, 0.0) - v) < 1e-9 for k, v in pub_bal.items()) and
            all(abs(pub_bal.get(k, 0.0) - v) < 1e-9 for k, v in bal.items()), "replay")
    C.check("ledger.conservation", all(abs(sum(v for (hh, k), v in bal.items() if k == kind) +
                                           sum(e["amount"] for e in esc.values() if e["kind"] == kind) - s) < 1e-6
                                       for kind, s in sup.items()) and sup == lg["supply"], "supply")
    C.check("ledger.no_negative_balance", all(v >= -1e-9 for v in bal.values()), "negative")
    C.check("ledger.no_real_money", not any(r.get("kind") in ("fiat", "real_money", "crypto", "bank_transfer")
                                            for r in lg["records"]), "real money")

    # ---- memory ----
    mm = ex["memory"]
    prev, oks = "genesis", True
    for i, e in enumerate(mm["entries"]):
        d = dig(D["imemory"], {**e, "prev": prev})
        oks &= d == mm["chain"][i]
        prev = d
    C.check("memory.chain_rederived", oks and len(mm["chain"]) == len(mm["entries"]), len(mm["entries"]))
    C.check("memory.no_policy_or_authority", all(e["kind"] not in mm["forbidden_kinds"] for e in mm["entries"]) and
            {"policy", "authority"} <= set(mm["forbidden_kinds"]), "kinds")
    C.check("memory.poison_quarantined", "INSTITUTIONAL_MEMORY_POISONING" in mm["poisoned_refusal"] and
            mm["quarantined"], mm["quarantined"])
    C.check("memory.decision_record_is_real", all(e["decision_ref"] == xe["decision_digest"] for e in mm["entries"]
                                                  if e["kind"] == "decision_record"), "decision ref")
    C.check("memory.evidence_entries_cite_evidence", all(e["evidence_refs"] for e in mm["entries"]
                                                         if e["kind"] == "evidence"), "refs")

    # ---- reputation ----
    rp = ex["reputation"]
    for s, sc in rp["scores"].items():
        v, n, col = reputation_score(rp["events"], rp["controllers"], s, rp["now"], rp["half_life"])
        C.check(f"reputation.{s}.rederived", v == sc["score"] and n == sc["distinct_controllers"] and
                col == sc["collusive_issuers"], (v, n, col))
    C.check("reputation.digests", all(h(strip(sc, "digest")) == sc["digest"] for sc in rp["scores"].values()), "digest")
    C.check("reputation.is_not_authority", all(sc["reputation_is_not_authority"] and sc["authority"] == "NONE"
                                               for sc in rp["scores"].values()), "NONE")
    C.check("reputation.no_self_issued", all(rp["controllers"][e["issuer"]] != rp["controllers"].get(e["subject"])
                                             and e["issuer"] != e["subject"] for e in rp["events"]), "self")
    C.check("reputation.sybil_counted_once", rp["scores"]["inst-logistics"]["distinct_controllers"] ==
            len({rp["controllers"][e["issuer"]] for e in rp["events"] if e["subject"] == "inst-logistics"}), "sybil")

    # ---- trust ----
    tr = ex["trust"]
    C.check("trust.values_rederived", all(trust_value(tr["edges"], q["src"], q["dst"], tr["now"], tr["attenuation"],
                                                      tr["max_depth"], tr["half_life"]) == q["value"]
                                          for q in tr["queries"]), [q["value"] for q in tr["queries"]])
    C.check("trust.bounded", all(q["hops"] <= tr["max_depth"] for q in tr["queries"]) and
            next(q for q in tr["queries"] if q["dst"] == "e")["value"] == 0.0, "depth")
    C.check("trust.is_not_authority", all(q["trust_is_not_authority"] and q["authority"] == "NONE"
                                          for q in tr["queries"]), "NONE")
    C.check("trust.cycles_flagged", any(s["kind"] == "CIRCULAR_VALIDATION" for s in tr["signals"]) and
            all(s["certainty"] is False for s in tr["signals"]), "cycles")

    # ---- dispute ----
    dp = ex["dispute"]
    res = dp["resolution"]
    C.check("dispute.signature", signed_ok(res, D["dispute"], dp["inst_pub"]), "ed25519")
    C.check("dispute.claims_preserved", res["claims_digest"] == h(dp["claims"]) and res["claims_preserved"] ==
            len(dp["claims"]), "claims")
    store = set(dp["evidence_store"])
    parties = sorted({c["party"] for c in dp["claims"]})
    ver = {p: len({e for c in dp["claims"] if c["party"] == p for e in c["evidence"] if e in store}) for p in parties}
    top = sorted(parties, key=lambda p: -ver[p])
    margin = ver[top[0]] - (ver[top[1]] if len(top) > 1 else 0)
    exp = f"IN_FAVOR_OF:{top[0]}" if margin > 0 else "UNRESOLVED"
    C.check("dispute.decision_rederived", res["decision"] == exp, (exp, res["decision"]))
    C.check("dispute.honest_uncertainty_and_appeal", res["appeal_state"] == "OPEN" and 0 <= res["uncertainty"] <= 1
            and res["grants_authority"] is False and res["authority_basis"]["resolver"] not in parties, "appeal")

    # ---- spawn, termination, dissolution ----
    br = ex["birth"]
    cert = br["certificate"]
    pol = cert["policy"]
    C.check("birth.signature", signed_ok(cert, D["birth"], br["inst_pub"]), "ed25519")
    C.check("birth.authority_bounded", set(cert["authority_granted"]) <= set(br["parent_authority_at_spawn"]) &
            set(pol["authority_allow"]), "authority")
    C.check("birth.capability_bounded", set(cert["capabilities"]) <= set(pol["capability_allow"]), "capabilities")
    C.check("birth.resources_bounded", all(v <= pol["resource_max"].get(k, 0.0) for k, v in cert["resources"].items()),
            "resources")
    C.check("birth.constraints_inherited", set(pol["required_constraints"]) | set(br["institution_constraints"]) <=
            set(cert["constraints"]), "constraints")
    C.check("birth.ttl_bounded", 0 < cert["expires_at"] - cert["issued_at"] <= pol["max_ttl"], "ttl")
    C.check("birth.mission_inherited", cert["mission"] == ident["mission"] and cert["authority"] == "NONE", "mission")
    tm = ex["termination"]
    rc = tm["receipt"]
    C.check("termination.signature", signed_ok(rc, D["termination"], tm["inst_pub"]), "ed25519")
    C.check("termination.cascade", rc["children_terminated"] and rc["credential_revoked"] and rc["tombstoned"] and
            rc["resources_recovered"] and rc["authority"] == "NONE", "cascade")
    dsn = tm["dissolution"]
    C.check("dissolution.signature", signed_ok(dsn, D["termination"], tm["dissolution_pub"]), "ed25519")
    C.check("dissolution.complete", dsn["members_terminated"] and dsn["treasury_recovered_to"] and
            dsn["authority"] == "NONE" and tm["threshold"] >= 2 and len(tm["governance_keys"]) >= tm["threshold"],
            "dissolution")

    # ---- federation, market, collusion, power, blast, twin, audit, supply ----
    fd = ex["federation"]
    inter = set.intersection(*[set(v["effective"]) for v in fd["members"].values()])
    uni = set().union(*[set(v["constraints"]) for v in fd["members"].values()])
    C.check("federation.authority_is_intersection", sorted(inter) == fd["authority"], sorted(inter))
    C.check("federation.policy_is_union", sorted(uni) == fd["policy"], sorted(uni))
    mk = ex["market"]
    al = mk["allocation"]
    C.check("market.allocation_digest", dig(D["market"], strip(al, "digest")) == al["digest"], "digest")
    C.check("market.simulated_and_grants_nothing", al["namespace"] == "MARKET_SIMULATION" and
            al["execution"] == "SIMULATED" and al["grants_authority"] is False, al["namespace"])
    C.check("market.winners_eligible", all(al["capability"] in mk["bidder_authority"][w["institution"]]
                                           for w in al["winners"]), "eligible")
    C.check("market.winners_lowest_price", [w["price"] for w in al["winners"]] ==
            sorted(w["price"] for w in al["winners"]), "price")
    cl = ex["collusion"]
    C.check("collusion.signal_digests", all(dig(D["collusion"], strip(s, "digest")) == s["digest"]
                                            for s in cl["signals"]), len(cl["signals"]))
    C.check("collusion.never_certain", cl["signals"] and all(s["certainty"] is False and s["confidence"] < 1 and
                                                             s["state"] in cl["states_allowed"] for s in cl["signals"])
            and cl["perfect_detection_claimed"] is False and cl["absence_of_signal"] == "UNKNOWN", "certainty")
    pw = ex["power"]
    C.check("power.digest", dig(D["power"], strip(pw, "digest")) == pw["digest"], "digest")
    C.check("power.observational_only", pw["observational"] is True and pw["decides_policy"] is False and
            pw["authority"] == "NONE", "observational")
    bl = ex["blast"]
    C.check("blast.digest", dig(D["blast"], strip(bl, "digest")) == bl["digest"], "digest")
    C.check("blast.epistemic_labels", all(v in EPISTEMIC for v in bl["affected"].values()) and
            bl["prediction_is_not_observation"] is True and bl["unknown"], "labels")
    tw = ex["twin"]
    C.check("twin.digest_and_non_executing", h(strip(tw, "digest")) == tw["digest"] and tw["executing"] is False and
            tw["layer"] == "SIMULATED" and tw["authority"] == "NONE", "twin")
    ad = ex["audit"]
    C.check("audit.digest", dig(D["audit"], strip(ad, "digest")) == ad["digest"], "digest")
    C.check("audit.reconstructs", all(k in ad for k in ("created_by", "why", "joined", "authority", "resources",
                                                        "contracts", "decisions", "actions", "changes", "failures",
                                                        "learned")) and ad["lineage_verified"] is True, "audit")
    sp = ex["supply_chain"]
    C.check("supply_chain.digest", dig(D["supply"], {"components": sp["components"], "deps": sp["deps"]}) ==
            sp["digest"], "digest")
    C.check("supply_chain.provenance_present", all(c["provenance"] for c in sp["components"].values()), "provenance")
    sm = ex["state_machine"]
    C.check("state_machine.ceiling_matches_independent_copy", {k: set(v) for k, v in sm["ceiling"].items()} == CEILING,
            "ceiling")
    C.check("state_machine.requirements_match", sm["requirements"] == REQUIREMENTS, "requirements")
    C.check("state_machine.terminal_dissolved", sm["transitions"]["DISSOLVED"] == [] and
            sm["transitions"]["DISSOLVING"] == ["DISSOLVED"], "terminal")
    C.check("state_machine.quorum_for_high_autonomy", set(sm["quorum_states"]) == {"AUTONOMOUS", "HIGH_AUTONOMY"},
            sm["quorum_states"])
    C.check("state_machine.containment_empties_authority", all(CEILING[s] == set() for s in
                                                               ("CONTAINED", "FROZEN", "DISSOLVING", "DISSOLVED")),
            "empty")

    # ---- records: invariants, bench, e2e, mutation, scale ----
    inv = load("INVARIANTS.json")
    ids = [c["id"] for c in inv["checks"]]
    C.check("invariants.at_least_100", inv["checked"] >= 100 and len(ids) == inv["checked"], inv["checked"])
    C.check("invariants.unique_ids", len(set(ids)) == len(ids), "ids")
    C.check("invariants.spec_fifty_present", all(f"I{i}" in ids for i in range(1, 51)), "I1..I50")
    C.check("invariants.all_hold", inv["all_hold"] and not inv["failed"] and all(c["holds"] for c in inv["checks"]),
            inv["failed"])
    bm = load("ATTACK_MANIFEST.json")
    at = bm["attacks"]
    C.check("bench.digests", all(dig(D["adversarial"], strip(a, "digest")) == a["digest"] for a in at.values()), len(at))
    C.check("bench.all_contained", bm["all_contained"] and all(a["contained"] for a in at.values()) and
            bm["contained"] == bm["total"] == len(at), bm["contained"])
    C.check("bench.distinct_minimum", bm["distinct_attacks"] >= 200 and
            bm["distinct_attacks"] == len(at) - len(bm["alias_entries"]), bm["distinct_attacks"])
    C.check("bench.categories_covered", all(bm["categories"].get(c, 0) >= 1 for c in REQUIRED_CATEGORIES),
            [c for c in REQUIRED_CATEGORIES if not bm["categories"].get(c)])
    C.check("bench.each_names_invariant_and_defense", all(a["expected_invariant"] in ids and a["defense"] and a["test"]
                                                          for a in at.values()), "fields")
    C.check("bench.no_authority_granted", all(a["authority"] == "NONE" for a in at.values()), "NONE")
    C.check("bench.real_world_validation_honest", bm["real_world_attack_validation"] == "NOT_PERFORMED", "honest")
    e2e = load("END_TO_END_RESULTS.json")
    steps = [s["step"] for s in e2e["steps"]]
    C.check("e2e.required_steps", all(s in steps for s in REQUIRED_E2E), [s for s in REQUIRED_E2E if s not in steps])
    C.check("e2e.all_steps_ok", e2e["all_steps_ok"] and all(s["ok"] for s in e2e["steps"]),
            [s["step"] for s in e2e["steps"] if not s["ok"]])
    C.check("e2e.hostile_events_caught", all(a in [x["attack"] for x in e2e["attacks"]] for a in REQUIRED_HOSTILE)
            and e2e["all_attacks_caught"] and all(x["detected"] and x["contained"] for x in e2e["attacks"]), "caught")
    C.check("e2e.execution_simulated", e2e["execution"] == "SIMULATED" and e2e["settlement"] == "SIMULATED",
            e2e["execution"])
    mu = load("MUTATION_RESULTS.json")
    base = mu["rows"][0]
    C.check("mutation.every_mutant_caught", mu["all_mutants_caught"] and len(mu["rows"]) >= 6 and all(
        r["bench_contained"] < base["bench_contained"] and r["invariants_holding"] < base["invariants_holding"]
        for r in mu["rows"][1:]), [r["mutant"] for r in mu["rows"][1:]])
    sc = load("SCALE_RESULTS.json")
    C.check("scale.sizes", {10, 100, 1000, 10000} <= {r["agents"] for r in sc["agents"]} and
            {10, 100, 1000} <= {r["institutions"] for r in sc["institutions"]}, "sizes")
    C.check("scale.honest_simulation", "not production-scale" in sc["note"] and "SIMULATIONS" in sc["note"] and
            all(r["virtual"] for r in sc["agents"] if r["agents"] >= 1000) and all(r["virtual"] for r in
                                                                                    sc["institutions"]), sc["note"][:60])
    C.check("scale.governance_held", all(r["authorized"] == r["executions"] and r["lineage_intact"]
                                         for r in sc["agents"]), "authorized/intact")
    C.check("scale.percentiles", all(all(k in r["member_authority"] for k in ("p50_us", "p95_us", "p99_us"))
                                     for r in sc["agents"]), "percentiles")
    es = load("ECONOMIC_SIMULATION_RESULTS.json")
    C.check("economy.market_isolated_and_conserved", all(r["governed_ledger_unchanged"] and not r["market_conservation"]
                                                         for r in es["market_rounds"]) and
            not es["governed_conservation"] and es["ledger_chain_intact"], "isolated")
    C.check("economy.contract_budget_enforced", [s["decision"] for s in es["governed_settlements"]] ==
            ["SETTLED", "SETTLED", "DENY"] and "CONTRACT_BUDGET_EXCEEDED" in es["governed_settlements"][2]["reasons"],
            [s["decision"] for s in es["governed_settlements"]])
    C.check("economy.honest_states", es["execution"] == "SIMULATED" and es["real_money"] == "NOT_IMPLEMENTED" and
            not any(s["real_money_moved"] for s in es["governed_settlements"]), "SIMULATED")
    C.check("economy.ineligible_excluded", any(x["reason"] == "INELIGIBLE_NO_AUTHORITY_OR_NOT_LIVE"
                                               for r in es["market_rounds"] for x in r["excluded"]), "excluded")
    cr = load("CONTRACT_RESULTS.json")
    C.check("contracts.results_consistent", cr["bench"]["contained"] == cr["bench"]["total"] > 0 and
            all(s["ok"] for s in cr["lifecycle"]), cr["bench"]["total"])
    co = load("COLLUSION_RESULTS.json")
    C.check("collusion.results_honest", co["perfect_detection_claimed"] is False and
            co["recall_against_real_adversaries"] == "UNKNOWN" and co["bench"]["contained"] == co["bench"]["total"],
            "honest")
    rr = load("RECOVERY_RESULTS.json")
    C.check("recovery.results_consistent", rr["recovery_mints_authority"] is False and all(s["ok"] for s in rr["e2e"])
            and all(c["holds"] for c in rr["invariants"]) and rr["bench"]["contained"] == rr["bench"]["total"],
            "recovery")
    te = load("TEST_RESULTS.json")
    C.check("tests.ran_and_passed", not te.get("skipped") and te.get("failed", 1) == 0 and te.get("errors", 1) == 0
            and te.get("passed", 0) > 0 and te.get("exit_code") == 0, te.get("summary"))

    # ---- claims, limitations, status ----
    cl = load("CAIN42_EVOLUTION20_CLAIMS.json")["claims"]
    ctext = json.dumps(cl).lower()
    C.check("claims.no_forbidden_words", not any(w in ctext for w in FORBIDDEN_WORDS),
            [w for w in FORBIDDEN_WORDS if w in ctext])
    C.check("claims.states_valid", all(c["state"] in CLAIM_STATES for c in cl), "states")
    by = {c["claim_id"]: c["state"] for c in cl}
    C.check("claims.real_economy_not_implemented", by.get("C42-E20-REAL-ECONOMY") == "NOT_IMPLEMENTED", "economy")
    C.check("claims.society_and_physical_not_implemented", by.get("C42-E20-SOCIETY-AND-PHYSICAL") == "NOT_IMPLEMENTED",
            "physical")
    C.check("claims.third_party_not_performed", by.get("C42-E20-THIRD-PARTY") == "NOT_PERFORMED", "third party")
    C.check("claims.scale_is_simulated", by.get("C42-E20-SCALE") == "SIMULATED", "scale")
    C.check("claims.tested_claims_have_evidence", all(c["evidence"] and all(
        (root / f"{e}.json").exists() or e == "CLEAN_ROOM_VERIFIER" for e in c["evidence"])
        for c in cl if c["state"] in ("TESTED", "SIMULATED")), "evidence files")
    li = load("LIMITATIONS.json")["limitations"]
    C.check("limitations.classified", len(li) >= 6 and all(x["status"] in ("NOT_IMPLEMENTED", "UNKNOWN", "UNVERIFIED",
                                                                           "NOT_PERFORMED") for x in li), len(li))
    sm2 = load("CAIN42_E20_EVIDENCE_BUNDLE.json")
    C.check("summary.counts_match", sm2["invariants"]["checked"] == inv["checked"] and sm2["attacks"]["total"] ==
            bm["total"] and sm2["attacks"]["distinct"] == bm["distinct_attacks"] and sm2["end_to_end"]["steps"] ==
            len(e2e["steps"]), "counts")
    gates = sm2["completion_gates"]
    C.check("summary.status_gated", (sm2["status"] == "COMPLETE") == all(v is True for v in gates.values()),
            sm2["status"])
    C.check("summary.honest_states", sm2["states"]["real_money"] == "NOT_IMPLEMENTED" and
            sm2["states"]["third_party_review"] == "NOT_PERFORMED" and sm2["states"]["hosted_service"] ==
            "NOT_IMPLEMENTED" and sm2["states"]["economy"] == "SIMULATED" and sm2["states"]["scale"] == "SIMULATED",
            "states")

    # ---- performance, provenance ----
    pf = load("PERFORMANCE.json")
    C.check("performance.conditions_honest", pf["conditions"]["concurrency"] == 1 and "not production-scale" in pf["note"],
            pf["note"])
    need = ("institution_creation", "membership_verification", "authority_compilation", "contract_verification",
            "resource_allocation", "delegation_verification", "institutional_graph_update", "trust_graph_update",
            "collusion_analysis", "blast_radius_analysis", "institutional_simulation", "state_checkpoint", "recovery",
            "dissolution")
    C.check("performance.operations_measured", all(k in pf["results"] and all(x in pf["results"][k] for x in
                                                                              ("p50_us", "p95_us", "p99_us",
                                                                               "throughput_per_s")) for k in need),
            [k for k in need if k not in pf["results"]])
    C.check("performance.hardware_reported", all(k in pf["conditions"] for k in ("cpu", "cpu_count", "python", "os",
                                                                                 "max_rss_kb", "dataset")), "hardware")
    pv = load("PROVENANCE.json")
    C.check("provenance.commit", bool(re.fullmatch(r"[0-9a-f]{40}", pv["commit"])), pv["commit"][:12])
    C.check("provenance.module_digests", all(re.fullmatch(r"[0-9a-f]{64}", v) for v in pv["modules"].values()) and
            "institution_fabric" in pv["modules"], "modules")

    # ---- clean room ----
    src = (root / "verify_e20.py.txt").read_text()
    mods = re.findall(r"^\s*(?:from|import)\s+([A-Za-z_][\w.]*)", src, re.M)
    C.check("clean_room.imports_no_cain", mods and not any(m.split(".")[0] in ("cain", "cain45", "platform_gateway")
                                                           for m in mods), sorted(set(mods)))
    C.check("clean_room.sha_matches", load("CLEAN_ROOM_VERIFIER.json")["sha256"] ==
            hashlib.sha256((root / "verify_e20.py.txt").read_bytes()).hexdigest(), "sha")

    out = {"schema": "cain42.e20.verifier.v1", "result": "INTACT" if not C.problems else "FAILED",
           "checks": len(C.detail), "passed": len(C.detail) - len(C.problems), "problems": C.problems,
           "detail": C.detail}
    print(json.dumps(out))
    return 0 if not C.problems else 1


if __name__ == "__main__":
    try:
        raise SystemExit(main(Path(sys.argv[1])))
    except (KeyError, TypeError, ValueError, StopIteration, AttributeError, json.JSONDecodeError,
            FileNotFoundError) as e:
        print(json.dumps({"schema": "cain42.e20.verifier.v1", "result": "FAILED", "checks": 0, "passed": 0,
                          "problems": [f"malformed bundle: {type(e).__name__}: {e}"]}))
        raise SystemExit(1)
