#!/usr/bin/env python3
"""Clean-room verifier for the CAIN-42 Evolution 18 4D spatial autonomy proof bundle.

IMPORTS NO CAIN-42 CODE. Standard library plus `cryptography` (Ed25519). From the published JSON alone it re-derives:
file hashes (MANIFEST), canonical serialization and domain-separated digests, the 4D world-state digest, entity
digests, reachable-set digests and the reachable/permitted/authorized separation, trajectory-field digests,
interaction-graph and conflict-field digests, time-to-consequence digests, geofence digests and signatures,
policy eligibility (never authorization), multimodal fusion, model arbitration and ensemble behaviour,
counterfactual future trees, actionability states, uncertainty propagation, the digital twin and reality gap, the
sixteen-digest spatial commit binding and the E8 canonical action hash, the Q-invariant matrix (>=89), the >=100
scenario bench, the end-to-end steps, and the master proof signature.

Every published governed object is {"body": <exactly the digested dict>, "digest": <domain-separated sha256>}.

Usage:  python3 verify_e18.py <bundle-directory>
"""
from __future__ import annotations

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

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

D = {"world": "CAIN42/E18-WORLD4D/v1", "entity": "CAIN42/E18-ENTITY4D/v1",
     "reachable": "CAIN42/E18-REACHABLE-SET/v1", "trajectory": "CAIN42/E18-TRAJECTORY-HYPOTHESIS/v1",
     "trajectory_field": "CAIN42/E18-TRAJECTORY-FIELD/v1", "intent": "CAIN42/E18-INTENT-HYPOTHESIS/v1",
     "interaction": "CAIN42/E18-SPATIAL-INTERACTION-GRAPH/v1", "conflict": "CAIN42/E18-CONFLICT-FIELD/v1",
     "ttc": "CAIN42/E18-TIME-TO-CONSEQUENCE/v1", "window": "CAIN42/E18-GOVERNANCE-WINDOW/v1",
     "geofence": "CAIN42/E18-GEOFENCE/v1", "policy": "CAIN42/E18-SPATIAL-POLICY/v1",
     "fusion": "CAIN42/E18-MULTIMODAL-FUSION/v1", "model_arb": "CAIN42/E18-MODEL-ARBITRATION/v1",
     "ensemble": "CAIN42/E18-PREDICTION-ENSEMBLE/v1", "futures": "CAIN42/E18-SPATIAL-FUTURE-TREE/v1",
     "actionability": "CAIN42/E18-ACTIONABILITY/v1", "action_set": "CAIN42/E18-GOVERNED-ACTION-SET/v1",
     "uncertainty": "CAIN42/E18-UNCERTAINTY-BUDGET/v1", "clock": "CAIN42/E18-GOVERNANCE-CLOCK/v1",
     "latency": "CAIN42/E18-LATENCY-BUDGET/v1", "twin": "CAIN42/E18-SPATIAL-DIGITAL-TWIN/v1",
     "gap": "CAIN42/E18-REALITY-GAP/v1", "replay": "CAIN42/E18-INCIDENT-REPLAY/v1",
     "commit": "CAIN42/E18-SPATIAL-COMMIT/v1", "adversarial": "CAIN42/E18-ADVERSARIAL/v1",
     "master": "CAIN42/E18-MASTER/v1", "e8_action": "CAIN42/E8-CANONICAL-ACTION/v1"}
BOUND_FIELDS = ("world_digest", "entity_digest", "reachable_digest", "trajectory_digest", "intent_digest",
                "interaction_digest", "conflict_digest", "ttc_digest", "window_digest", "authority_digest",
                "capability_digest", "policy_digest", "risk_digest", "consequence_digest", "uncertainty_digest",
                "command_digest")
REQUIRED_FILES = ("MANIFEST.json", "SCHEMAS.json", "CAIN42_EVOLUTION18_PROOF.json",
                  "CAIN42_EVOLUTION18_MASTER_PROOF.json", "CAIN42_EVOLUTION18_CLAIMS.json", "MACHINE_STATUS.json",
                  "WORLD_STATE_4D_EXAMPLES.json", "ENTITY_EXAMPLES.json", "REACHABILITY_EXAMPLES.json",
                  "TRAJECTORY_FIELD_EXAMPLES.json", "INTERACTION_GRAPH_EXAMPLES.json", "CONFLICT_EXAMPLES.json",
                  "AIRSPACE_EXAMPLES.json", "ROADSPACE_EXAMPLES.json", "GEOFENCE_EXAMPLES.json",
                  "COUNTERFACTUAL_EXAMPLES.json", "ACTIONABILITY_EXAMPLES.json", "UNCERTAINTY_EXAMPLES.json",
                  "INCIDENT_REPLAY_EXAMPLES.json", "COMMIT_EXAMPLES.json", "END_TO_END_DEMO.json",
                  "ATTACK_MANIFEST.json", "TEST_VECTORS.json", "PERFORMANCE.json", "LIMITATIONS.json",
                  "LIMITATIONS.md", "CLEAN_ROOM_VERIFIER.json", "verify_e18.py.txt", "REPRODUCE.txt", "index.html")
FORBIDDEN = ("private_key", "private key", "-----begin", "secret_key", "password")
INTENTS = ("CONTINUE", "STOP", "YIELD", "TURN", "MERGE", "ACCELERATE", "DECELERATE", "ALTITUDE_CHANGE", "LAND",
           "TAKE_OFF", "REROUTE", "CROSS", "APPROACH", "DEPART", "HOVER", "RETURN")
ACTIONABILITY_STATES = ("ACTIONABLE", "CONDITIONALLY_ACTIONABLE", "REQUIRES_REVALIDATION", "REQUIRES_HUMAN",
                        "SAFE_STATE_ONLY", "DENIED", "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_b64: str, sig_b64: str, domain: str, fields: dict) -> bool:
    try:
        ed25519.Ed25519PublicKey.from_public_bytes(base64.b64decode(pub_b64)).verify(
            base64.b64decode(sig_b64), dig(domain, fields).encode())
        return True
    except (InvalidSignature, ValueError, TypeError):
        return False


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


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

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

    def digest(self, name: str, domain: str, obj: dict) -> None:
        ok = isinstance(obj, dict) and "body" in obj and dig(domain, obj["body"]) == obj.get("digest")
        self.check(name, ok, str(obj.get("digest", ""))[:16])


def main(d: Path) -> int:
    C = Checker()
    load = lambda n: json.loads((d / n).read_text())
    manifest = load("MANIFEST.json")
    presentation = set(manifest.get("unhashed_presentation", [])) | {"index.html"}
    bad = [n for n, want in manifest["files"].items()
           if n not in presentation and (not (d / n).exists()
                                         or hashlib.sha256((d / n).read_bytes()).hexdigest() != want)]
    C.check("manifest_file_hashes", not bad, ",".join(bad))
    C.check("required_files_present", all((d / f).exists() for f in REQUIRED_FILES)
            and all(f in manifest["files"] for f in REQUIRED_FILES if f not in ("MANIFEST.json", "index.html")),
            "missing required")
    C.check("presentation_page_unhashed", presentation <= {"index.html"}, str(sorted(presentation)))

    tv = load("TEST_VECTORS.json")
    C.check("vectors.canonical_json", hashlib.sha256(canon(tv["canonical_json"]["input"])).hexdigest()
            == tv["canonical_json"]["sha256"], tv["canonical_json"]["sha256"])
    sep = tv["domain_separation"]
    C.check("vectors.domain_separation", len(set(sep["digests"].values())) == len(sep["digests"])
            and all(dig(n["domain"], sep["fields"]) == sep["digests"][n["domain"]] for n in tv["domains"]),
            str(list(sep["digests"].values()))[:80])
    C.check("vectors.negative_cases", tv["negative"]["prediction_not_reality"] is True
            and tv["negative"]["reachability_not_permission"] is True
            and tv["negative"]["actionability_not_authorization"] is True
            and tv["negative"]["unknown_not_allow"] is True, str(tv["negative"]))

    # ---- world + entities ----
    we = load("WORLD_STATE_4D_EXAMPLES.json")
    C.digest("world.digest", D["world"], we["world"])
    C.check("world.reality_kind", we["world"]["body"]["kind"] == "REALITY", we["world"]["body"]["kind"])
    C.check("world.history_replayable", we["written_empty"] is True
            or we["history_root"] == we["world"]["history_root"], "history")
    ee = load("ENTITY_EXAMPLES.json")
    for e in ee["entities"]:
        C.digest(f"entity.digest.{e['body']['entity_id']}", D["entity"], e)
    ids = sorted(e["body"]["entity_id"] for e in ee["entities"])
    C.check("entity.expected_set", ids == sorted(ee["expected_ids"]), str(ids))
    C.check("entity.uncertainty_not_collapsed", all(e["body"]["uncertainty"] >= 0 and len(e["body"]["covariance"]) == 3
            for e in ee["entities"]), "uncertainty")
    C.check("entity.reality_layers", all(e["body"]["layer"] in ("OBSERVED", "VERIFIED", "DERIVED", "INFERRED")
            for e in ee["entities"]), "layers")

    # ---- reachability ----
    re_ = load("REACHABILITY_EXAMPLES.json")
    C.digest("reachable.digest", D["reachable"], re_["reachable"])
    rb = re_["reachable"]["body"]
    auth = {tuple(x) for x in rb["authorized"]}
    perm = {tuple(x) for x in rb["policy_permitted"]}
    phys = {tuple(x) for x in rb["physically_reachable"]}
    C.check("reachable.subsets", auth <= perm <= phys, "subsets")
    C.check("reachable.not_permission", re_["reachable"].get("authority", re_["reachable"]["body"].get("authority", "NONE")) == "NONE", "authority")

    # ---- trajectory field ----
    te = load("TRAJECTORY_FIELD_EXAMPLES.json")
    C.digest("trajectory.digest", D["trajectory"], te["hypothesis"])
    C.check("trajectory.not_fact", te["hypothesis"]["body"]["layer"] == "PREDICTED"
            and te["hypothesis"]["is_fact"] is False, te["hypothesis"]["body"]["layer"])
    C.check("trajectory.probability_bounded", 0.0 <= te["hypothesis"]["body"]["probability"] <= 1.0, "prob")

    # ---- interaction / conflict ----
    ie = load("INTERACTION_GRAPH_EXAMPLES.json")
    C.digest("interaction.digest", D["interaction"], ie["graph"])
    C.check("interaction.relations_valid", all(r in
            ("APPROACHING", "FOLLOWING", "CROSSING", "MERGING", "OVERTAKING", "CONVERGING", "SEPARATING", "FORMATION",
             "CONFLICT", "DEPENDENCY", "COOPERATIVE", "POTENTIAL_COLLISION") for r in ie["graph"]["body"]["relations"]),
            "relations")
    ce = load("CONFLICT_EXAMPLES.json")
    C.digest("conflict.digest", D["conflict"], ce["clear"])
    C.digest("conflict.detected.digest", D["conflict"], ce["detected"])
    C.check("conflict.not_distance_only", ce["detected"]["body"]["distance_is_not_safety"] is True
            and ce["detected"]["body"]["count"] >= 1, "kinds")
    C.check("conflict.authority_none", ce["clear"].get("authority", ce["clear"]["body"].get("authority", "NONE")) == "NONE", "authority")

    # ---- time to consequence ----
    tt = load("CONFLICT_EXAMPLES.json")["ttc"]
    C.digest("ttc.digest", D["ttc"], tt)
    C.check("ttc.has_uncertainty", all(v["state"] in ("ESTIMATED", "UNKNOWN") for v in tt["body"]["ttc"].values())
            and ("uncertainty" in tt["body"]["ttc"]["time_to_collision"]), "uncertainty")

    # ---- geofence / policy ----
    ge = load("GEOFENCE_EXAMPLES.json")
    C.digest("geofence.digest", D["geofence"], ge["geofence"])
    C.check("geofence.signature", sig_ok(ge["geofence"]["issuer"], ge["geofence"]["signature_b64"], D["geofence"],
            ge["geofence"]["body"]), "sig")
    C.check("geofence.provenance", bool(ge["geofence"]["body"]["provenance"]), "prov")
    C.check("geofence.expiry", ge["contains_now"] is True and ge["contains_later"] is False, "expiry")
    pe = load("ACTIONABILITY_EXAMPLES.json")["policy"]
    C.digest("policy.digest", D["policy"], pe)
    C.check("policy.eligibility_not_authorization", pe["body"]["eligible"] is True
            and pe["body"]["eligible_is_not_authorized"] is True, pe["body"]["eligible"])

    # ---- fusion / model arbitration / ensemble ----
    am = load("ACTIONABILITY_EXAMPLES.json")
    fu = am["fusion"]
    C.digest("fusion.digest", D["fusion"], fu)
    C.check("fusion.conflict_requires_review", fu["body"]["agreement"] is False
            and fu["body"]["decision"] == "REQUIRES_REVIEW"
            and fu["body"]["conflicting_safety_critical_resolves_to_allow"] is False, fu["body"]["decision"])
    ma = am["arbitration"]
    C.digest("model_arb.digest", D["model_arb"], ma)
    C.check("model_arb.not_highest_confidence", ma["body"]["highest_confidence_chosen"] is False
            and ma["body"]["selected"] == "b", ma["body"]["selected"])
    en = am["ensemble"]
    C.digest("ensemble.digest", D["ensemble"], en)
    C.check("ensemble.correlated_errors", en["body"]["correlated_errors"] is True
            and en["body"]["authority"] == "NONE", en["body"]["correlated_errors"])

    # ---- counterfactual future tree ----
    fe = load("COUNTERFACTUAL_EXAMPLES.json")
    C.digest("futures.digest", D["futures"], fe["tree"])
    C.check("futures.never_reality", fe["tree"]["body"]["mutates_reality"] is False
            and all(b["is_fact"] is False for b in fe["tree"]["branches_detail"]), "counterfactual")
    C.check("futures.branches", set(b["name"] for b in fe["tree"]["branches_detail"]) ==
            {"CONTINUE", "STOP", "SLOW", "YIELD", "REROUTE", "ALTITUDE_CHANGE", "LAND", "SAFE_STATE"}, "branches")

    # ---- actionability ----
    ab = am["actionable"]
    C.digest("actionability.digest", D["actionability"], ab)
    C.check("actionability.state_valid", ab["body"]["state"] in ACTIONABILITY_STATES
            and ab["body"]["actionability_is_not_authorization"] is True and ab["body"]["authority"] == "NONE",
            ab["body"]["state"])
    C.check("actionability.denied_without_authority",
            am["denied"]["body"]["state"] == "DENIED" and am["safe_state"]["body"]["state"] == "SAFE_STATE_ONLY",
            "states")
    aset = am["action_set"]
    C.digest("action_set.digest", D["action_set"], aset)
    C.check("action_set.domain_actions", "continue" in aset["body"]["actions"], str(aset["body"]["actions"]))
    sel = am["selection"]
    C.digest("selection.digest", "CAIN42/E18-ACTION-SELECTION/v1", sel)
    C.check("selection.is_proposal", sel["body"]["selection_is_a_proposal"] is True
            and sel["body"]["authority"] == "NONE", "proposal")

    # ---- uncertainty / clock / latency ----
    ue = load("UNCERTAINTY_EXAMPLES.json")
    C.digest("uncertainty.budget.digest", D["uncertainty"], ue["budget"])
    C.check("uncertainty.response_bounded", ue["budget"]["body"]["response"] in
            ("REAUTHORIZE", "DEGRADE", "SAFE_STATE", "HUMAN_REVIEW", "NONE"), ue["budget"]["body"]["response"])
    C.digest("uncertainty.propagation.digest", D["uncertainty"], ue["propagation"])
    C.check("uncertainty.monotone", ue["propagation"]["body"]["monotone"] is True, "monotone")
    C.digest("clock.digest", D["clock"], ue["clock"])
    C.check("clock.freshness", ue["clock_fresh"] is False, "fresh")
    C.digest("latency.digest", D["latency"], ue["latency"])
    C.check("latency.safe_degrade", ue["latency"]["body"]["verdict"] == "SAFE_DEGRADE"
            and ue["latency"]["body"]["bypass_governance"] is False, ue["latency"]["body"]["verdict"])

    # ---- twin / gap / replay ----
    C.digest("twin.digest", D["twin"], ue["twin"])
    C.check("twin.layers_not_conflated", ue["twin"]["body"]["conflated"] is False
            and set(ue["twin"]["body"]["layers"]) == {"REALITY", "OBSERVED", "MODEL", "PREDICTED", "SIMULATED",
                                                      "AUTHORIZED", "EXECUTED"}, "layers")
    C.digest("gap.digest", D["gap"], ue["gap"])
    C.check("gap.invalidates", ue["gap"]["body"]["material_divergence"] is True
            and ue["gap"]["body"]["invalidates_authorization"] is True, "gap")
    rp = load("INCIDENT_REPLAY_EXAMPLES.json")
    C.digest("replay.digest", D["replay"], rp["complete"])
    C.digest("replay.partial.digest", D["replay"], rp["partial"])
    C.check("replay.immutable", rp["complete"]["body"]["replayable"] is True
            and rp["complete"]["body"]["immutable_evidence"] is True and rp["partial"]["body"]["replayable"] is False,
            "replay")

    # ---- commit binding ----
    cme = load("COMMIT_EXAMPLES.json")
    committed, denied = cme["committed"], cme["denied"]
    C.check("commit.bound_fields", sorted(committed["body"]["bound_fields"]) == sorted(BOUND_FIELDS)
            and len(committed["body"]["bound_fields"]) == 16, str(len(committed["body"]["bound_fields"])))
    C.digest("commit.digest", D["commit"], committed)
    C.digest("commit.denied_digest", D["commit"], denied)
    C.check("commit.authorized_through_e8", committed["body"]["decision"] == "AUTHORIZED"
            and committed["authority"] == "E8" and committed["body"]["reasons"] == [], committed["body"]["decision"])
    C.check("commit.denied_without_e8", denied["body"]["decision"] == "DENY"
            and any("E8_BOUNDARY_NOT_CONFIGURED" in r for r in denied["body"]["reasons"]), str(denied["body"]["reasons"]))
    # the verdicts the committed action was bound to must be the ones shown, and they must PERMIT it
    v, bnd = cme["committed_verdicts"], committed["body"]["bindings"]
    C.check("commit.verdicts_bound", dig(D["policy"], strip(v["policy"], "digest")) == v["policy"]["digest"] == bnd["policy_digest"]
            and dig(D["actionability"], strip(v["consequence"], "digest")) == v["consequence"]["digest"] == bnd["consequence_digest"]
            and h(v["risk"]) == bnd["risk_digest"]
            and dig(D["uncertainty"], strip(v["uncertainty"], "digest")) == v["uncertainty"]["digest"] == bnd["uncertainty_digest"]
            and v["authority"]["digest"] == bnd["authority_digest"], "verdict digests")
    st = list(v["uncertainty"]["stages"].values())
    risk_score = v["risk"].get("score")
    C.check("commit.verdicts_permit", v["policy"]["eligible"] is True and v["consequence"]["state"] == "ACTIONABLE"
            and isinstance(risk_score, (int, float)) and risk_score == risk_score
            and risk_score <= v["thresholds"]["risk"] and all(a <= b for a, b in zip(st, st[1:]))
            and st[-1] <= v["thresholds"]["uncertainty"] and v["authority"]["revoked"] is False, "verdicts")
    dbv = cme["denied_by_verdict"]
    C.digest("commit.denied_by_verdict_digest", D["commit"], dbv)
    C.check("commit.consistent_reissue_of_refusal_denied", dbv["body"]["decision"] == "DENY"
            and dbv["authority"] == "NONE" and "POLICY_NOT_ELIGIBLE" in dbv["body"]["reasons"], str(dbv["body"]["reasons"]))
    C.check("commit.e8_action_bound", cme["e8_action"]["action_hash"] == dig(D["e8_action"],
            cme["e8_action"]["body"]), cme["e8_action"]["action_hash"][:16])
    C.check("commit.e8_action_carries_bindings", sorted(cme["e8_action"]["body"]["parameters"]) == sorted(BOUND_FIELDS),
            "parameters")

    # ---- e2e ----
    e2e = load("END_TO_END_DEMO.json")
    C.check("e2e.all_steps_ok", e2e["all_steps_ok"] is True and all(s["ok"] for s in e2e["steps"]), "e2e")
    C.check("e2e.all_mutations_governed", e2e["all_mutations_governed"] is True
            and all(m["invalidated"] for m in e2e["mutations"]), "mutations")
    C.check("e2e.control_authorizes", e2e.get("control_authorized") is True
            and "baseline" not in {m["mutation"] for m in e2e["mutations"]}, "control")
    C.check("e2e.required_steps", {"PERCEPTION", "EVIDENCE", "IDENTITY", "WORLD_STATE_CREATION", "PREDICTION",
            "REACHABLE_SETS", "TRAJECTORY_HYPOTHESES", "INTERACTION_GRAPH", "CONSEQUENCE_PREDICTION", "ACTIONABILITY",
            "AUTHORITY", "POLICY", "AUTHORIZATION", "E8_COMMIT", "SIMULATED_EXECUTION", "NEW_OBSERVATION",
            "WORLD_STATE_MUTATION", "AUTHORIZATION_INVALIDATION", "RE_EVALUATION", "SAFE_STATE_TRANSITION"}
            <= {s["step"] for s in e2e["steps"]}, "steps")

    # ---- invariants ----
    proof = load("CAIN42_EVOLUTION18_PROOF.json")
    inv = proof["invariants"]
    C.check("invariants.count", inv["checked"] >= 75 and len(inv["checks"]) == inv["checked"], str(inv["checked"]))
    C.check("invariants.ids", [x["id"] for x in inv["checks"]] == [f"Q{i:02d}" for i in range(1, inv["checked"] + 1)],
            "ids")
    C.check("invariants.boundary_review", {f"Q{i}" for i in range(81, 90)} <= {x["id"] for x in inv["checks"]}, "Q81-Q89")
    C.check("invariants.all_hold", inv["all_hold"] is True and not inv["failed"], str(inv["failed"]))
    C.check("invariants.entries_hold", all(x.get("holds") is True for x in inv["checks"]),
            str([x["id"] for x in inv["checks"] if not x.get("holds")]))
    C.check("invariants.failed_consistent",
            list(inv["failed"]) == [x["id"] for x in inv["checks"] if not x.get("holds")], str(inv["failed"]))

    # ---- bench ----
    bm = load("ATTACK_MANIFEST.json")
    C.check("bench.minimum", bm["total"] >= 100 and len(bm["attacks"]) == bm["total"], str(bm["total"]))
    distinct = [n for n in bm["attacks"] if not n.startswith("invariant_scenario_")]
    C.check("bench.distinct_minimum", len(distinct) >= 100 and bm["distinct_attacks"] == len(distinct), str(len(distinct)))
    C.check("bench.all_contained", bm["all_contained"] is True and bm["contained"] == bm["total"],
            f"{bm['contained']}/{bm['total']}")
    C.check("bench.honest", bm["real_world_attack_validation"] == "NOT_PERFORMED" and bm["authority"] == "NONE", "honest")
    C.check("bench.entries_contained", all(v["contained"] is True and v["state"] == "CONTAINED"
            for v in bm["attacks"].values()), "entries")
    C.check("bench.required_classes", {"gps_spoofing_position_jump", "sensor_disagreement", "geofence_mutation",
            "model_substitution", "authorization_resurrection", "toctou", "simulation_reality_confusion",
            "compromised_planner", "safe_state_bypass", "drone_vehicle_interaction"} <= set(bm["attacks"]), "classes")

    # ---- performance / limitations / claims / status / schemas ----
    perf = load("PERFORMANCE.json")
    C.check("performance.conditions", perf["conditions"]["concurrency"] == 1
            and perf["conditions"]["entity_count"] >= 5, "conditions")
    C.check("performance.results", perf["results"] and all("p50_us" in v and "p99_us" in v and "max_us" in v
            for v in perf["results"].values()), "results")
    lim = load("LIMITATIONS.json")
    C.check("limitations.classified", len(lim["limitations"]) >= 5 and all(x["status"] in
            ("NOT_IMPLEMENTED", "UNKNOWN", "UNVERIFIED", "NOT_PERFORMED") for x in lim["limitations"]), "limitations")
    C.check("limitations.no_physical_claim", any(x["status"] == "NOT_IMPLEMENTED" for x in lim["limitations"]), "honest")
    cl = load("CAIN42_EVOLUTION18_CLAIMS.json")
    C.check("claims.states_valid", all(c["state"] in ("IMPLEMENTED", "TESTED", "VERIFIED", "SIMULATED", "UNVERIFIED",
            "UNKNOWN", "NOT_IMPLEMENTED", "NOT_PERFORMED") for c in cl["claims"]), "claims")
    C.check("claims.no_autonomous_driving", any(c["state"] == "NOT_IMPLEMENTED" for c in cl["claims"]), "claims honest")
    ms = load("MACHINE_STATUS.json")
    C.check("status.no_physical_claim", ms["states"]["real_world_integration"] == "NOT_IMPLEMENTED"
            and ms["states"]["hardware_attestation"] == "UNKNOWN" and ms["states"]["third_party_review"] == "NOT_PERFORMED",
            str(ms["states"]))
    C.check("status.counts_match", ms["invariants_checked"] == inv["checked"] and ms["attacks_total"] == bm["total"],
            "counts")
    sc = load("SCHEMAS.json")
    C.check("schemas.present", len(sc["schemas"]) >= 25 and "ActionableWorld4D" in sc["schemas"], str(len(sc["schemas"])))
    crv = load("CLEAN_ROOM_VERIFIER.json")
    C.check("clean_room.imports_no_cain", crv["imports_cain"] is False and crv["checks"] >= 60, "clean room")
    C.check("clean_room.sha_matches", hashlib.sha256((d / "verify_e18.py.txt").read_text().encode()).hexdigest()
            == crv["verifier_sha256"], "verifier sha")

    # ---- master proof signature ----
    master = load("CAIN42_EVOLUTION18_MASTER_PROOF.json")
    mbody = strip(master, "signature_b64", "signer_public_key_b64")
    C.check("master.signature", sig_ok(master["signer_public_key_b64"], master["signature_b64"], D["master"], mbody),
            "sig")
    C.check("master.says_tested", master["evidence_level"] == "TESTED" and master["signing_key_class"] == "EPHEMERAL",
            master["evidence_level"])
    C.check("master.no_overclaim", master["status"]["REAL_WORLD_INTEGRATION"] == "NOT_IMPLEMENTED"
            and master["status"]["HARDWARE_ATTESTATION"] == "UNKNOWN", str(master["status"]))
    C.check("master.counts_match", master["invariants_checked"] == inv["checked"]
            and master["attacks_total"] == bm["total"], "counts")
    blob = "".join((d / n).read_text(errors="ignore").lower() for n in manifest["files"] if n.endswith(".json"))
    C.check("no_forbidden_material", not any(f in blob for f in FORBIDDEN), "forbidden material present")

    passed = sum(1 for c in C.checks if c["result"] == "PASS")
    out = {"schema": "cain42.e18.verifier.v1", "result": "INTACT" if not C.problems else "FAILED",
           "checks": len(C.checks), "passed": passed, "problems": C.problems, "detail": C.checks}
    print(json.dumps(out))
    return 0 if not C.problems else 1


if __name__ == "__main__":
    raise SystemExit(main(Path(sys.argv[1])))
