"""RUN-018 - Round 15: global proof capacity and the threshold ladder.

Round 15 turns claim capacities into a capacity for the whole proof: a node's capacity is
the better of its best direct claim and the weaker of its two children
(C(p) = max(D(p), min(C(p0), C(p1))), Thm 6.1); the global capacity is the lesser of the
root's and the evidence's; any target up to it is a free promotion; beyond it, a deficit
frontier and a minimum-cost repair DP say what to recompute. A phase-master root at
T_M = 0.8365 is proposed. The reference computes the capacity of Round 10's local proof.
Here the recursion, feasibility and the repair DP are checked against brute force on
random trees, the reference is recomputed, its root capacity is examined, and the phase
root's constants are rebuilt.

Checks
  A0 anchors      the formulas and numbers below occur in Round 15 as written
  W1 capacity     Thm 6.1 and sec. 7, 11-12 against brute force over all proof routes
  W2 repair       sec. 19-20: the DP against brute force over all repair plans
  W3 reference    sec. 13-18: leaf and node capacities, breakpoints, deficits, the
                  limiting claims, against the snapshot and the text
  W4 exclusion    the local proof's second root: the B7 lift square sized at t_7(0.835);
                  above 0.835 a sliver of translations is proved by nothing
  W5 phase        sec. 24-26: d_*(0.8365), t_3, t_5, t_7, the ratios; the four-body optimum
  P1 package      the protocol against the text
  P2 package      the package's self-description of its files matches the archive
  R1 registry     Round 15's judgments reviewed, first-used statuses defined

Usage: python code/run018_round15_capacity_ladder.py [--drill] [--blind CID]
"""
import argparse
import copy
import itertools
import json
import math
import re

import numpy as np

import gate
import registry
from corpus import SOURCES, TREE, source_bytes, walk_sources
from run009_round06_witness_exchange import hull_pieces, placed_reuleaux
from run010_round07_minimizer_atlas import d_star, t_text
from run012_round09_adaptive_atlas import base_pieces
from run017_round14_semantic_rebase import capacity

PKG = "main.zip!/AMRAL_LUC_FC_Round_15_Package_v0.1.zip!/"
REF = PKG + "AMRAL_LUC_FC_Round_15_reference_capacity/"
DOC = PKG + "AMRAL_Lebesgue_Finite_Closure_Round_15_Proof_Capacity_Ladder_v0.1.md"
PROTOCOL = PKG + "AMRAL_LUC_FC_Round_15_PROOF_CAPACITY_LADDER_PROTOCOL_v0.1.md"
CRYSTAL = PKG + "AMRAL_LUC_FC_State_Crystal_Round_15_v0.1.md"
VALIDATION = PKG + "AMRAL_LUC_FC_Round_15_VALIDATION.json"
MASTER = REF + "master_domain_08365.json"
SNAPSHOT = REF + "reference_capacity_snapshot.json"
PLANNER = REF + "reference_capacity_planner.py"
HOT = "main.zip!/AMRAL_LUC_FC_Round_14_Package_v0.1.zip!/AMRAL_LUC_FC_Round_14_reference_compaction/hot_proof_core.json"
R10_MANIFEST = "main.zip!/AMRAL_LUC_FC_Round_10_Package_v0.1.zip!/AMRAL_LUC_FC_Round_10_reference_cert/manifest.json"
RUN010 = TREE / "data" / "gate-logs" / "run010-round07-minimizer-atlas.json"
LOG = TREE / "data" / "gate-logs" / "run018-round15-capacity-ladder.json"
MANIFEST = TREE / "data" / "SOURCE_MANIFEST.v1.json"

T0 = 0.835
ANCHORS = {
    "W1": [r"C(p)=\max\left\{D(p),\min\{C(p0),C(p1)\}\right\}.",
           r"C_{\mathrm{global}}=\min\{C_{\mathrm{root}},C_{\mathrm{evidence}}\}."],
    "W2": [r"K_T(p)=\min\left\{J_T(p),K_T(p0)+K_T(p1)\right\}."],
    "W3": [r"C(\epsilon)=0.8350003545377508.", r"C_{\mathrm{ref}}=0.8350003545377508.", r"\Delta_T=\{10\}.",
           r"\{00,10,11\}.", r"\{00,01,10,11\}"],
    "W5": [r"d_\star\approx0.699860544061.", r"t_3\approx0.201023529007,", r"t_5\approx0.202618286230,",
           r"t_7\approx0.201473445786.", r"\approx1.03165,", r"\approx1.02425,", r"\approx1.02305.", r"11.66\%.",
           r"4.66\%.", r"T_M=0.8365.", r"0.836494901."],
}


# ---------------------------------------------------------------- capacity algebra

def node_capacity(p, D, nodes, rec=None):
    """sec. 4: C(p) = max(D(p), min(C(p0), C(p1))) where both children are nodes"""
    d = D.get(p, -math.inf)
    if p + "0" in nodes and p + "1" in nodes:
        return max(d, min(node_capacity(p + "0", D, nodes), node_capacity(p + "1", D, nodes)))
    return d


def routes(p, nodes):
    """every way to prove B(p): p directly, or a route for each child"""
    yield (p,)
    if p + "0" in nodes and p + "1" in nodes:
        for a in routes(p + "0", nodes):
            for b in routes(p + "1", nodes):
                yield a + b


def brute_capacity(p, D, nodes):
    return max(min(D.get(q, -math.inf) for q in r) for r in routes(p, nodes))


def repair_dp(p, D, J, nodes, T):
    """sec. 19: K_T(p) = 0 if already feasible, else min(J_T(p), K_T(p0) + K_T(p1))"""
    if node_capacity(p, D, nodes) >= T:
        return 0.0
    if p + "0" in nodes and p + "1" in nodes:
        return min(J[p], repair_dp(p + "0", D, J, nodes, T) + repair_dp(p + "1", D, J, nodes, T))
    return J[p]


def brute_repair(p, D, J, nodes, T):
    """the cheapest set of recertified nodes after which the root is feasible"""
    best = math.inf
    names = sorted(nodes)
    for k in range(len(names) + 1):
        for chosen in itertools.combinations(names, k):
            cost = sum(J[q] for q in chosen)
            if cost >= best:
                continue
            D2 = dict(D)
            for q in chosen:
                D2[q] = max(D2.get(q, -math.inf), T)
            if node_capacity(p, D2, nodes) >= T:
                best = cost
    return best


def random_tree(rng, depth):
    nodes, frontier = {""}, [""]
    while frontier:
        p = frontier.pop()
        if len(p) < depth and rng.uniform() < 0.7:
            nodes |= {p + "0", p + "1"}
            frontier += [p + "0", p + "1"]
    return nodes


# ---------------------------------------------------------------- checks

def w1_capacity(w):
    out, rng = [], np.random.default_rng(151)
    for k in range(200):
        nodes = random_tree(rng, 4)
        D = {p: float(rng.uniform(0, 1)) for p in nodes if rng.uniform() < 0.6}
        c, b = w["capacity_rule"]("", D, nodes), brute_capacity("", D, nodes)
        if c != b:
            out.append(f"tree {k}: recursion {c}, best route {b}")
            break
        for T in (c - 1e-9, c + 1e-9):              # sec. 11: feasible iff T <= C
            feasible = any(all(D.get(q, -math.inf) >= T for q in r) for r in routes("", nodes))
            if feasible != (T <= c):
                out.append(f"tree {k}: feasibility at {T} is {feasible}, capacity {c}")
                break
    return out


def w2_repair(w):
    out, rng = [], np.random.default_rng(153)
    for k in range(120):
        nodes = random_tree(rng, 3)
        D = {p: float(rng.uniform(0, 1)) for p in nodes if rng.uniform() < 0.5}
        J = {p: float(rng.uniform(0.1, 1.0)) for p in nodes}
        T = float(rng.uniform(0.3, 1.0))
        dp, bf = w["repair_rule"]("", D, J, nodes, T), brute_repair("", D, J, nodes, T)
        if abs(dp - bf) > 1e-12:
            out.append(f"tree {k}: DP {dp}, cheapest plan {bf}")
            break
    return out


def reference_capacities(w):
    claims = w["hot"]["active_claims"]
    leaf = {}
    for c in claims:
        for s in c["seed_paths"]:
            leaf[s] = max(leaf.get(s, -math.inf), capacity(c, w["snapshot"]["error_budget"]))
    return leaf


def w3_reference(w):
    out, snap = [], w["snapshot"]
    leaf = reference_capacities(w)
    nodes = {"", "0", "1", *leaf}
    root = w["capacity_rule"]("", leaf, nodes)
    if leaf != snap["leaf_capacities"] or root != snap["evidence_capacity"] or root != w["text_capacity"]:
        out.append(f"leaf capacities {leaf}, root {root!r}; snapshot {snap['leaf_capacities']}, {snap['evidence_capacity']}")
    for p in ("0", "1", ""):
        if w["capacity_rule"](p, leaf, nodes) != snap["node_strategy"][p]["capacity"]:
            out.append(f"node {p!r}: capacity differs from the snapshot's")
    if sorted(set(leaf.values())) != snap["capacity_breakpoints"]:
        out.append("the breakpoints differ")
    owner = {}
    for c in w["hot"]["active_claims"]:
        for s in c["seed_paths"]:
            owner[s] = c["claim_id"]
    for row in snap["target_table"]:
        T = row["target"]
        deficit = sorted(s for s, k in leaf.items() if k < T)
        limiting = sorted({owner[s] for s in deficit})
        if (root >= T) != row["existing_evidence_ready"] or deficit != row["deficit_seed_paths"] \
                or limiting != row["currently_limiting_claim_ids"]:
            out.append(f"target {T}: rebuilt deficit {deficit}, snapshot {row['deficit_seed_paths']}")
        if T in w["text_deficits"] and deficit != w["text_deficits"][T]:
            out.append(f"target {T}: the text's deficit {w['text_deficits'][T]}")
    return out


def w4_exclusion(w):
    """the local proof's B7 lift square covers |x_7|, |y_7| <= t_7(0.835); a placement outside
    it is excluded only by the a-priori bound, which at |t| just beyond t_7(0.835) is just
    above 0.835. For a target T above 0.835 the translations with t_7(0.835) < |t| <= t_7(T)
    on the square's axes are proved by neither the lift trees nor the exclusion."""
    out = []
    t7 = w["r10_manifest"]["witness"]["translation_square_halfwidth"]
    R7 = 1 / (2 * math.cos(math.pi / 14))
    phi = lambda d: math.pi / 4 + 0.5 * math.sqrt(d * d - 0.25) - 0.25 * math.acos(0.5 / d)
    apriori = lambda t: phi(math.sqrt(t * t + R7 * R7 + 2 * t * R7 * math.cos(math.pi / 7)))
    root_capacity = apriori(t7)
    rows = {}
    for T in w["promoted_targets"]:
        tT = t_text(7, d_star(T))
        mid = (t7 + tT) / 2
        rows[str(T)] = {"t7_at_target": tT, "sliver_width": tT - t7, "apriori_in_sliver": apriori(mid),
                        "covered": apriori(mid) >= T}
    q = np.array(w["r10_manifest"]["base_domain"]["center"], dtype=float)
    mid = (t7 + t_text(7, d_star(max(w["promoted_targets"])))) / 2
    actual = min(hull_pieces(base_pieces(q) + placed_reuleaux(7, a, (mid, 0.0)), 2048)[0]
                 for a in np.linspace(0, 2 * math.pi / 7, 32))
    supported = {str(T): r["covered"] for T, r in rows.items()}
    if abs(root_capacity - T0) > 1e-12 or supported != w["promotion_supported"]:
        out.append(f"lift-root capacity {root_capacity!r}; promotions supported {supported}, recorded "
                   f"{w['promotion_supported']}")
    w["w4"] = {"lift_root_capacity": root_capacity, "targets": rows, "actual_hull_in_sliver_least": actual,
               "reference_says_root_capacity": w["snapshot"]["root_domain_capacity"]}
    return out


def w5_phase(w):
    out, m = [], w["master"]
    TM = m["phase_master_target"]
    d = d_star(TM)
    t = {n: t_text(n, d) for n in (3, 5, 7)}
    t0 = {n: t_text(n, d_star(T0)) for n in (3, 5, 7)}
    got = {"d_star": d, "Reuleaux3": t[3], "Reuleaux5": t[5], "Reuleaux7": t[7]}
    rec = {"d_star": m["d_star"], **m["translation_radius"]}
    for k, v in got.items():
        if abs(v - rec[k]) > 5e-13 or abs(v - w["text_phase"][k]) > 5e-13:
            out.append(f"{k}: rebuilt {v!r}, recorded {rec[k]!r}, text {w['text_phase'][k]}")
    ratios = {3: t[3] / t0[3], 5: t[5] / t0[5], 7: t[7] / t0[7]}
    vol4, sq7 = ratios[3] ** 2 * ratios[5] ** 2, ratios[7] ** 2
    c = m["comparison_to_0_835"]
    for key, val in (("R3_halfwidth_ratio", ratios[3]), ("R5_halfwidth_ratio", ratios[5]),
                     ("R7_halfwidth_ratio", ratios[7]), ("F3_translation_4volume_ratio", vol4),
                     ("B7_translation_square_ratio", sq7)):
        if abs(val - c[key]) > 1e-12:
            out.append(f"{key}: rebuilt {val!r}, recorded {c[key]!r}")
    if abs(vol4 - 1 - 0.1166) > 5e-5 or abs(sq7 - 1 - 0.0466) > 5e-5:
        out.append("the text's 11.66% or 4.66% differs")
    four = w["run010"]["w4"]["four_body_search"]
    below = four < TM and 0.836494901 < TM
    if below != w["four_body_below_phase"]:
        out.append(f"the four-body optimum {four} against the phase target {TM}")
    w["w5"] = {"t": got, "volume_ratios": [vol4, sq7], "four_body_optimum_run010": four,
               "four_body_logged": 0.836494901, "phase_target": TM}
    return out


def p1_package(w):
    out = []
    for phrase in (r"C_{\mathrm{global}}", r"\min\{C(p0),C(p1)\}", r"K_T(p0)+K_T(p1)", "T_M=0.8365", "11.7", "4.7"):
        if phrase not in re.sub(r"\s+", "", w["protocol"]):
            out.append(f"the protocol lacks {phrase}")
    return out


def p2_package(w):
    out, leaves = [], {row[0]: row for row in w["manifest"]["leaves"]}
    v = w["validation"]
    for rec, leaf_id, text in ((v["round15"], DOC, w["text"]), (v["state_crystal"], CRYSTAL, w["crystal"]),
                               (v["protocol"], PROTOCOL, w["protocol"])):
        _, size, digest = leaves[leaf_id]
        if rec["sha256"] != digest or rec["bytes"] != size:
            out.append(f"{rec['file']}: recorded sha256/bytes differ from the archive")
        n = text.count("$$")
        if rec["double_dollar_count"] != n or rec["double_dollar_even"] != (n % 2 == 0):
            out.append(f"{rec['file']}: recorded {rec['double_dollar_count']} $$, document has {n}")
        if rec["contains_unicode_escape_sequence"] != bool(re.search(r"\\u[0-9a-fA-F]{4}", text)):
            out.append(f"{rec['file']}: unicode-escape flag differs")
        if rec["contains_bracket_math_delimiter"] != bool(re.search(r"\\[\[\]()]", text)):
            out.append(f"{rec['file']}: bracket-delimiter flag differs")
    if v["planner_sha256"] != leaves[PLANNER][2]:
        out.append("planner_sha256: recorded digest differs from the archive")
    if v["master_domain"] != w["master"] or v["reference_capacity_snapshot"] != w["snapshot"]:
        out.append("VALIDATION's copies differ from the files")
    return out


# ---------------------------------------------------------------- registry (Round 15)

TOKEN = "a status label of sec. 0, 15, 26 or 34, defined there; not a judgment"
R15_DEFINITIONS = {
    "FORMALIZED + REFERENCE TESTED": ("in-line", "sec. 37: formalized, and exercised on the reference capacities"),
    "FREE-PROMOTABLE": ("in-line", "sec. 34: a target no higher than the global capacity"),
    "MIXED-BOTTLENECK": ("in-line", "sec. 34: both the root and the evidence fall short of the target"),
    "PASS / FAIL": ("in-line", "sec. 0: the binary claim record Round 14 replaced with capacities"),
    "PHASE-DESIGN-CHOICE": ("in-line", "sec. 26: an engineering choice of root target, not a theorem"),
    "READY": ("in-line", "sec. 15: the planner's report that existing evidence supports a target"),
    "ROOT-BOTTLENECK": ("in-line", "sec. 34: the root's capacity is below both the target and the evidence's"),
    "SHARD-BOTTLENECK": ("in-line", "sec. 34: the evidence's capacity is below both the target and the root's"),
}
R15_REVIEWS = [
    ("PROVED", "Recursive evidence capacity", "analytic", "PASS",
     "Thm 6.1: the recursion equals the best of all proof routes (W1)"),
    ("PROVED", "Global capacity theorem", "analytic", "PASS",
     "min(C_root, C_evidence), provided C_root covers every a-priori exclusion the proof uses, the witness lift "
     "roots included; the reference omits the B7 root's (W4)"),
    ("PROVED", "Free promotion", "analytic", "PASS",
     "sound under a complete C_root; the reference's promotion to 0.83500035 is not (W4)"),
    ("PROVED", "Minimum-cost repair DP", "analytic", "PASS", "equals the cheapest plan by exhaustion (W2)"),
    ("PASS", "Reference capacity planner", "computation", "PARTIAL",
     "the evidence capacities, breakpoints and deficits rebuild exactly (W3), but it takes the local root's capacity "
     "as infinite: the B7 lift square was sized at t_7(0.835), so the local proof's capacity is 0.835 and its "
     "'READY' at 0.83500035 is unsupported (W4)"),
    ("CLOSED", "PROOF CAPACITY ALGEBRA", "analytic", "PASS", "W1, W2"),
    ("CLOSED", "THRESHOLD LADDER AUTOMATION", "reading", "PARTIAL",
     "the ladder promotes by evidence capacity; without every exclusion's capacity in C_root it promotes targets the "
     "proof does not cover, as the reference shows (W4)"),
    ("COMPUTE-DEFERRED", "Global $0.8350$ heavy certificate", "n/a", "n/a", ""),
    ("STILL COMPUTE-DEFERRED", "GLOBAL 0.8350 HEAVY CERTIFICATE", "n/a", "n/a",
     "sec. 0's boxed summary; read from census v3 on (RUN-003)"),
    ("FORMALIZED", "Root capacity", "reading", "PASS",
     "sec. 1-2; right, and it applies to each witness lift root as much as to the base root"),
    ("FORMALIZED", "Phase-master root", "reading", "PASS",
     "W5; the constants include t_7(0.8365); the four-body optimum lies below 0.8365, so in the B7 phase the root "
     "never binds"),
    ("IMPLEMENTED", "Deficit frontier", "computation", "PASS", "W3"),
    ("FORMALIZED + REFERENCE TESTED", "Capacity staircase", "computation", "PASS",
     "W1, W3: the staircase and its breakpoints rebuild; like every evidence figure of the reference, its steps above "
     "0.835 still wait on the lift root's capacity (W4); read from census v4 on (RUN-003)"),
    ("FREE-PROMOTABLE", "", "n/a", "n/a", TOKEN),
    ("MIXED-BOTTLENECK", "", "n/a", "n/a", TOKEN),
    ("PASS / FAIL", "0. 本輪摘要判定", "n/a", "n/a", TOKEN),
    ("PHASE-DESIGN-CHOICE", "26. Why 0.8365 is only a phase design", "n/a", "n/a", TOKEN),
    ("READY", "15. Reference Free Promotion", "n/a", "n/a", TOKEN + "; the READY in question is unsupported (W4)"),
    ("ROOT-BOTTLENECK", "", "n/a", "n/a", TOKEN),
    ("SHARD-BOTTLENECK", "", "n/a", "n/a", TOKEN),
]


def r1_registry(w):
    return registry.completeness(w["registry"], "R15", w["definitions"], w["reviews"])


# ---------------------------------------------------------------- world, checks, drill

CHECKS = {"A0": gate.anchors_check(ANCHORS), "W1": w1_capacity, "W2": w2_repair, "W3": w3_reference,
          "W4": w4_exclusion, "W5": w5_phase, "P1": p1_package, "P2": p2_package, "R1": r1_registry}
DEPENDS = {"A0": {"text"}, "W1": {"capacity_rule"}, "W2": {"repair_rule"},
           "W3": {"hot", "snapshot", "capacity_rule", "text_capacity", "text_deficits"},
           "W4": {"r10_manifest", "snapshot", "promoted_targets", "promotion_supported"},
           "W5": {"master", "text_phase", "run010", "four_body_below_phase"}, "P1": {"protocol"},
           "P2": {"validation", "text", "crystal", "protocol", "manifest", "master", "snapshot"},
           "R1": {"registry", "definitions", "reviews"}}

CLAIMS = {
    "capacity_rule": node_capacity, "repair_rule": repair_dp,
    "text_capacity": 0.8350003545377508,
    "text_deficits": {0.83500036: ["10"], 0.83500039: ["00", "10", "11"], 0.83500096: ["00", "01", "10", "11"]},
    "promoted_targets": [0.8350001, 0.83500035],
    "promotion_supported": {"0.8350001": False, "0.83500035": False},
    "text_phase": {"d_star": 0.699860544061, "Reuleaux3": 0.201023529007, "Reuleaux5": 0.202618286230,
                   "Reuleaux7": 0.201473445786},
    "four_body_below_phase": True,
}


def load_inputs():
    blobs = {n: source_bytes(n) for n in SOURCES}
    want = {DOC, PROTOCOL, CRYSTAL, VALIDATION, MASTER, SNAPSHOT, HOT, R10_MANIFEST}
    got = {leaf.id: d for leaf, d in walk_sources(blobs, keep=lambda l: l.id in want) if d is not None}
    return {"text": got[DOC].decode("utf-8"), "protocol": got[PROTOCOL].decode("utf-8"),
            "crystal": got[CRYSTAL].decode("utf-8"), "validation": json.loads(got[VALIDATION]),
            "master": json.loads(got[MASTER]), "snapshot": json.loads(got[SNAPSHOT]), "hot": json.loads(got[HOT]),
            "r10_manifest": json.loads(got[R10_MANIFEST]), "run010": json.loads(RUN010.read_text(encoding="utf-8")),
            "manifest": json.loads(MANIFEST.read_text(encoding="utf-8"))}


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--drill", action="store_true")
    ap.add_argument("--blind", metavar="CID")
    args = ap.parse_args()
    inputs = load_inputs()
    fns = ("capacity_rule", "repair_rule")

    def build():
        return {**copy.deepcopy(inputs), "registry": registry.load(), "definitions": dict(R15_DEFINITIONS),
                "reviews": list(R15_REVIEWS), **copy.deepcopy({k: v for k, v in CLAIMS.items() if k not in fns}),
                **{k: CLAIMS[k] for k in fns}}

    if args.blind:
        CHECKS[args.blind] = lambda w: []
        args.drill = True
    w = build()
    results = {c: fn(w) for c, fn in CHECKS.items()}
    for c, f in results.items():
        print(f"{c} {'PASS' if not f else 'FAIL'}", *(f"\n     {x}" for x in f[:6]))
    for key in ("w4", "w5"):
        print(key, w.get(key))
    if not args.drill:
        return
    cases = [
        ("K0-unmodified", "control", {}, set()),
        ("K1-doc-crlf", "control", {"text": inputs["text"].replace("\n", "\r\n")}, set()),
        ("K2-capacity-by-routes", "control", {"capacity_rule": brute_capacity}, set()),
        ("K3-repair-by-exhaustion", "control", {"repair_rule": brute_repair}, set()),
        ("M1-doc-root-capacity", "mutation",
         {"text": inputs["text"].replace("C_{\\mathrm{ref}}\n=\n0.8350003545377508", "C_{\\mathrm{ref}}\n=\n0.8350003545377518")},
         {"A0"}),
        ("M2-capacity-min-max-swapped", "mutation",
         {"capacity_rule": lambda p, D, N: (min(D.get(p, math.inf), max(node_capacity(p + "0", D, N), node_capacity(p + "1", D, N)))
                                            if p + "0" in N and p + "1" in N else D.get(p, -math.inf))}, {"W1", "W3"}),
        ("M3-repair-always-split", "mutation",
         {"repair_rule": lambda p, D, J, N, T: (0.0 if node_capacity(p, D, N) >= T else
                                                 (repair_dp(p + "0", D, J, N, T) + repair_dp(p + "1", D, J, N, T)
                                                  if p + "0" in N and p + "1" in N else J[p]))}, {"W2"}),
        ("M4-text-deficit", "mutation", {"text_deficits": {**CLAIMS["text_deficits"], 0.83500036: ["00", "10"]}}, {"W3"}),
        ("M5-promotion-assumed-supported", "mutation", {"promotion_supported": {"0.8350001": True, "0.83500035": True}},
         {"W4"}),
        ("M6-phase-t7-digit", "mutation", {"text_phase": {**CLAIMS["text_phase"], "Reuleaux7": 0.201473445796}}, {"W5"}),
        ("M7-four-body-above-phase", "mutation", {"four_body_below_phase": False}, {"W5"}),
        ("M8-protocol-dp-dropped", "mutation", {"protocol": inputs["protocol"].replace("K_T(p0)+K_T(p1)", "K_T(p0)")},
         {"P1"}),
        ("M9-planner-digest", "mutation", {"validation": {**inputs["validation"], "planner_sha256": "0" * 64}}, {"P2"}),
        ("M10-review-dropped", "mutation", {"reviews": list(R15_REVIEWS[1:])}, {"R1"}),
    ]
    if args.blind:
        cases = [c for c in cases if args.blind in c[3]]
    d = gate.run_drill(build, CHECKS, DEPENDS, cases)
    summary = gate.summarize(d, CHECKS)
    print(gate.report_drill(summary))
    for x in d:
        if not x["ok"]:
            print("   DRILL FAIL", x)
    if args.blind:
        n = sum(1 for x in d if args.blind in x["expected"] and args.blind not in x["tripped"])
        print(f"blind {args.blind}: drill reported it blind in {n} cases (nothing written)")
        return
    log = {"run": "RUN-018", "subject": "Round 15: global proof capacity and the threshold ladder", "document": DOC,
           "checks": {c: {"pass": not f, "failures": f} for c, f in results.items()},
           **{k: w.get(k) for k in ("w4", "w5")}, "drill": summary}
    LOG.write_text(json.dumps(log, ensure_ascii=False, indent=1, default=float) + "\n", encoding="utf-8")
    if not results["R1"]:
        registry.record("R15", "RUN-018", R15_DEFINITIONS, R15_REVIEWS)
        print(f"registry: {len(R15_DEFINITIONS)} definitions, {len(R15_REVIEWS)} reviews recorded for R15")


if __name__ == "__main__":
    main()
