#!/usr/bin/env python3
r"""
kite-threshold-evaluate.py -- threshold-configuration kite: standalone
arbitrary-precision evaluation of the eps^0 top master TOP(t) from the EXPLICIT
symbolic closure.  mpmath-only (sympy/flint not required).  No AMFlow, no Kira,
no stored numerics in the value path.

THE EXPLICIT FORM (all ingredients written out; nothing fit):

  d/ds M(s) = [A0(s) + eps*A1(s)] M(s)                                (exact rational IBP
      connection, 22 masters, vendored as integer coefficient lists in
      kite-threshold-connection.json; letters {s, s-1, s-9, s-25, 85-4s, s^2-6s+1, s^2-22s+49})

  M(0) = V(eps)   -- the p^2=0 point, a regular singular point of the system.  The residue
      matrix A_{-1} = Res_{s=0} A has exponents {0 (x8), -1+eps (x13), -2+2eps (x1)}:
      every non-analytic branch is unbounded, so the bounded physical solution is the
      UNIQUE analytic branch.  (The family has its massless line on the rung; both three-line
      cuts contain it, so no sub-topology carries an elliptic curve -- the 'Kodaira Type III'
      wording of earlier versions referred to a (1,2,3) sunrise that is not in this family.)
      V(eps) is CLASSICAL: partial-fractioning the p^2=0 degenerate
      propagator pairs collapses every master to
         T1(m^2)  = -Gamma(-1+eps) (m^2)^{1-eps}                      (1-loop tadpole)
         T(a,b,0) = -Gamma(eps)Gamma(1-eps)/(1-eps) * a^{1-2eps} *
                    [ Gamma(2eps-1)(1-r)^{1-2eps}
                      - r^{1-eps} (Gamma(eps)/Gamma(2-eps)) 2F1(eps,1;2-eps;r) ],  r=b/a
      (one-massless-line 2-loop vacuum sunset; derived by Mellin-Barnes + the reflection
      collapse Gamma(1-eps-n)Gamma(n+eps) = (-1)^n Gamma(eps)Gamma(1-eps); its eps-series
      is logs/Li2 level and is generated live below to any order).
      V(eps) lies in ker A_{-1} -- asserted at runtime (analytic-branch consistency).

  TOP(t) = the (1,1,1,1,1) component of the analytic-branch Frobenius solution
      transported along the pole-free Euclidean axis.  Equivalently, unfolding the
      block-triangular hierarchy, 4t*TOP(t) is the explicit iterated-integral (VoP word
      list) over the rational letters above with classical boundary constants; the
      homogeneous constants of every level are killed by boundedness at p^2=0 (the
      analytic, vacuum-seeded starting data -- no fit anywhere).

  Frame decomposition (--AB), kept for the record: with (psi1,psi2) the BMSW period pair
      of a (1,2,3)-mass sunrise curve -- NOT a sub-topology of this family -- A,B are the
      coordinates of (F, F') at t0=-2 in that frame, F=4t*TOP:
      A = (F psi2' - F' psi2)/W,  B = (F' psi1 - F psi1')/W,  W = psi1 psi2' - psi2 psi1',
      F' DE-exact from row 21 of the connection.

Held-out gate (never used in the construction): the seven reference AMFlow oracle
points t = -1,-2,-3,-4,-5,-6,-8 of the original computation (vendored below as
gate-cache, wired as a RAISING gate on every evaluated table point).

Usage:
  python3 kite-threshold-evaluate.py                    # quick demo: one gate point
                                                        #   + A,B, both at dps 30
  python3 kite-threshold-evaluate.py --full             # 6 gate points at dps 100
  python3 kite-threshold-evaluate.py --dps 100 --point=-1 --point=-7/2
  python3 kite-threshold-evaluate.py --dps 100 --check  # re-runs at dps+60, diffs
  python3 kite-threshold-evaluate.py --dps 100 --AB     # derive the frame constants A,B
  python3 kite-threshold-evaluate.py --dps 100 --AB-ring # the certified A,B strings (420 digits,
                                                        #   n_cert 326) + the PSLQ rings as pins,
                                                        #   then A,B re-derived and compared
  python3 kite-threshold-evaluate.py --masses 1,4,9,3,0                 # the (1,4,9,3,0) line: quick demo
                                                        #   (t=-2 at dps 30, 22-entry reference table)
  python3 kite-threshold-evaluate.py --masses 1,4,9,3,0 --dps 145 --point=-2 --point=-7/3 --check
                                                        #   the two reference points, bar 98, dps+60 rerun

CHANGELOG:
  2026-07-05: per-step certified trailing-window tail gate in stepf
    (step rule |h| <= 0.3*dmin => Taylor ratio r <= 0.3; tail <= max |c_n h^n|
    over the last TAIL_WINDOW=8 terms, all 66 state components, * r/(1-r);
    `order` demoted to STARTING seed, doubled by EXACT continuation of the same
    recurrences to a 16x cap, RuntimeError at cap) + the same certified
    last-term gate on the s=0 Frobenius sum (radius of convergence rho =
    3-2*sqrt(2) certified => r = |s1|/rho; Nser demoted to starting seed, same
    growth/cap/raise).  Per-step bounds accumulate into a certified per-point
    BOUND line (TOP_eps0 has exact prefactor 1 in the state vector, so l1
    propagation is the plain sum).  --check FAIL now exits nonzero.  Values on
    the non-escalating path are bit-identical to the pre-edit script.
  2026-07-06: (a) --AB frame constants now use EXACT ANALYTIC
    differentiation of the period construction (chain rule through rad/e_i/
    Z_i/k2 + dK/dm = (E-(1-m)K)/(2m(1-m)); derivation comment at the code) --
    the old central finite difference h=10^-(dps//3) carried ~h^2 truncation
    that froze the dps30-vs-60 A,B agreement at ~21 digits; a Legendre-relation
    closed form for the Wronskian W = -pi*k2'/(Z3*k2*(1-k2)) is enforced as a
    RAISING cross-check.  (b) INDEPENDENT ORACLE GATE: the seven reference
    AMFlow values (gate-cache strings vendored below; recompute path = the
    construction above is the definition) are compared against every evaluated
    table point; disagreement below min(dps-8, oracle_digits-2) digits raises
    (rc!=0).  (c) SEED INTEGRITY GATE: the classical seed blocks T1/T are
    recomputed through the SAME series code at dps+40 and compared at numeric
    eps=1e-9 against an independent direct evaluation (mp.gamma/mp.hyp2f1);
    certified trailing-window truncation tail (rho_eps = 1/2), refine-until-
    bound with doubling + 16x cap, tolerance 10^-(dps+10), RuntimeError on
    mismatch.  This catches seed mutations >= ~1e-(dps+10) at ANY working
    precision -- the ker A_{-1} residual provably cannot (a 1e-30 perturbation
    of the 2F1 term alone leaves the kernel residual at 6.4e-62: the t2 piece
    lies in ker A_{-1} separately).  Values on the clean path are
    bit-identical to the 2026-07-05 script except A,B beyond digit ~21
    (oracle-justified: old FD values agree with the analytic ones to exactly
    the h^2 truncation, and the analytic values crank-track dps).
  2026-07-06b: (a) FAIL-CLOSED DOMAIN GUARD: the transport
    walks strictly DOWNWARD from s1 (default -0.02); a target t > s1
    previously spun the step loop FOREVER (h < 0 while the target sits above
    x; measured rc=124 under timeout at t=-19/1000).  Such targets now raise
    DomainRefusal BEFORE any heavy work; main() prints the named refusal and
    exits rc=2.  t == s1 stays legal (zero transport steps).  (b) --point-only:
    plain point evaluation -- skips the always-on control batteries (seed
    integrity gate) and the oracle gate-table compare; the VALUE PATH is
    untouched (classical seed, ker A_{-1} analytic-branch assert, certified
    Frobenius tail, per-step certified transport bounds all retained), so the
    printed value and BOUND lines are bit-identical to a bare --point run.
    Requires --point; mutually exclusive with --check (the batteries remain
    default-on for --check and bare runs).  Defaults byte-identical.
  2026-09-05: --masses M1,M2,M3,M4,M5 -- the five squared masses of the lines
    D1 = k1^2 - M1, D2 = (k1-p)^2 - M2, D3 = k2^2 - M3, D4 = (k2-p)^2 - M4,
    D5 = (k1-k2)^2 - M5.  Everything above is the line 1,4,9,2,0 (the
    default; --masses 1,4,9,2,0 explicit == the default, every path
    byte-identical).  --masses 1,4,9,3,0 selects the line with the fourth
    squared mass 3: its own exact rational connection
    kite-threshold-connection-m3sq3.json (the same 22 masters in the same
    order, the same nonzero pattern; letters {s, s-1, s-9, s-25, 297-8s,
    s^2-8s+4, s^2-24s+36}, singular points 0, 1, 9, 25, 297/8, 4 -+ 2 sqrt3,
    12 -+ 6 sqrt3 -- the negative axis pole-free; Frobenius radius
    4 - 2 sqrt3 = 0.536), the SAME classical p^2=0 seed construction with the
    masses as parameters (seed_vector_general: tadpoles T1 at 1, 4, 9, 3,
    sunsets T at the pairs (1,9), (4,9), (1,3), (4,3), partial-fraction
    weights 1/(4-1) = 1/3 and 1/(9-3) = 1/6, numerator shifts 3 and 6 --
    at the default masses this twin reproduces seed_vector bit for bit, a
    control run at every new-line start), the same ker A_{-1} assert, the
    same seed integrity gate (seed_integrity_gate_general, the direct
    Gamma/2F1 compare at the new masses), the same certified Frobenius +
    transport.  Two gate files ship with the line, sha256-pinned (a byte
    change is REFUSED, exit 3, the recorded and recomputed pins named):
    kite-threshold-seed-m3sq3.json (the seed table computed independently
    by the shipped mass-general seed script kite-threshold-seed-general.py,
    55 digits; the live seed must agree to >= 50 digits, RAISING) and
    kite-threshold-reference-m3sq3.json (the INDEPENDENT REFERENCE: AMFlow at
    t = -2 and t = -7/3, twenty-two (master, eps order) entries per point --
    fifteen compared by digits, seven exact rationals; gate only, never an
    input).  At a reference point the --point tier prints the 22-entry
    table with the agreement recomputed per entry and a RAISING bar: the
    fifteen digit-compared entries >= min(98, dps-8) digits (98 = the
    floor of the independent gate of record, 98.74 digits at its dps 100;
    -8 = this script's roundoff margin, the one the oracle gate uses) and
    the seven rational entries equal to their rational within 10^-(dps-8);
    --check reruns at dps+60 and, at the new line, also compares all 22
    entries two-precision (RAISING at dps-8).  The reference strings hold
    109-110 digits, so the measurable agreement caps near 109 digits.  Bare
    --masses 1,4,9,3,0 = the quick demo of the line: --point=-2 at dps 30.
    --full and --AB are the 1,4,9,2,0 record (its six-point oracle table;
    its frame decomposition at t0 = -2) and are refused at the new line
    (exit 2); --point-only, --order, --s1 apply as above.  Measured walls
    (this host, a shared 96-core server at loadavg ~130; wall clock,
    /usr/bin/time): quick demo 32 s; --dps 100 --point=-2
    --point=-7/3 --check 633 s; --dps 145 --point=-2 --point=-7/3
    --check 1083 s.  The 1,4,9,2,0 tiers are unchanged (default
    66 s, --dps 100 --point=-1 --point=-7/2 216 s, the same with
    --check 673 s, --dps 100 --AB 198 s, --full 267 s).
  2026-09-06: --AB-ring -- the certified frame constants and the PSLQ rings as
    PINS; the value path (derive_AB, the seed block, every tier above) is
    untouched: --dps 100 --AB and every other tier print byte for byte what
    they printed before.  kite-threshold-AB-ring.json ships beside this
    script, sha256-pinned in PINS (a byte change is REFUSED, exit 3; the
    file missing, exit 4; both named).  It carries the four strings of the
    --AB tier (A, B, F(-2), F'(-2)) at working precision 420, certified to
    n_cert = 326 digits = the agreement of the two highest working
    precisions (330 vs 420: 328.6 digits at the least) minus 2 guard digits
    -- a working precision is never a certificate; the record's literals
    beside (they agree with the strings to about 75 digits, the record's own
    finite-difference floor: no certified digit of A or B moves); and the
    three rings scanned by PSLQ for an integer relation among A, B, A/B,
    A varpi - B pi, F, F', A sqrt(3 sqrt57), B sqrt(3 sqrt57): REC (12
    members: the record's ring of pi, varpi, ln2, ln3), T0 (23: the frame
    curve's own period ring at t0 = -2 -- the Q-span of 1, sqrt57,
    sqrt(3 sqrt57), their product, and the weight <= 2 monomials in pi, K,
    K', E, E' at m = 1/2 + 5 sqrt57/114, K K' kept out of the pool as the
    Legendre positive control), T1 (33: T0 plus varpi, pi/varpi, their
    squares, ln2, ln3, their squares and product, zeta(3)); NULL in every
    ring: to height 10^12 / 10^10 / 10^6 under the house capacity law and
    10^12 / 10^6 / 10^4 under the strict law, two-precision stable, a
    planted relation refound and a pseudo-random negative control null at
    every rung; the receipts' sha256 prefixes are the pins.  --AB-ring
    prints all of that, then RE-DERIVES A, B, F, F' by the derive_AB above
    at --dps (default 100, the --AB tier's) and compares each with its
    pinned string: the agreement printed capped at min(n_cert, dps - 8), the
    bar the served two-precision form dps - 8 (capped at n_cert above --dps
    334), FAIL by name (exit 1) below it.  --mutate-ring alters one digit of
    one pinned string in memory (decimal digit 10 of A, or half the cap when
    the cap is smaller: below the bar at every served --dps (>= 30; --AB-ring refuses lower); the file's pin
    untouched): the compare FAILS by name.  --AB and --AB-ring together, or --mutate-ring without
    --AB-ring, are refused (exit 2); at --masses 1,4,9,3,0 --AB-ring is
    refused like --AB.  Wall = the --AB tier's at the same --dps plus the
    file read; the measured figures are in CHANGES.md.
"""
import os, sys, json, time, argparse


class DomainRefusal(RuntimeError):
    """Requested target outside the reachable transport domain (fail-closed).

    The vacuum-seeded transport starts at s1 and steps strictly downward
    (h < 0); targets above s1 are unreachable and used to loop forever
    (fixed 2026-07-06).  main() converts this to a named refusal
    with exit code 2."""

HERE = os.path.dirname(os.path.abspath(__file__))

# ---------------------------------------------------------------------------
# Mass-line selection (2026-09-05).  --masses names the five squared masses
# (M1, M2, M3, M4, M5) of (D1, D2, D3, D4, D5); the fifth line is massless in
# both shipped lines (the seed construction partial-fractions the p^2 = 0
# degenerate pairs (D1, D2) and (D3, D4) against a massless D5).  The default
# line 1,4,9,2,0 is everything below unchanged; the line 1,4,9,3,0 selects its
# own connection, seed table, independent reference, pole list and Frobenius
# radius, the data files sha256-pinned.  Every entry of a config evaluates, at
# the default, to the literal the script carried before this block existed.
# ---------------------------------------------------------------------------
MASS_CONFIGS = {
    (1, 4, 9, 2, 0): {
        "conn": "kite-threshold-connection.json",
        "poles": lambda mp: [mp.mpf(0), mp.mpf(1), mp.mpf(9), mp.mpf(25), mp.mpf(85)/4,
                             3-2*mp.sqrt(2), 3+2*mp.sqrt(2), 11-6*mp.sqrt(2), 11+6*mp.sqrt(2)],
        "rho": lambda mp: 3 - 2*mp.sqrt(2),
        "poles_text": "s = 0, 1, 9, 25, 85/4, 3 -+ 2 sqrt2, 11 -+ 6 sqrt2",
    },
    (1, 4, 9, 3, 0): {
        "conn": "kite-threshold-connection-m3sq3.json",
        "seed_table": "kite-threshold-seed-m3sq3.json",      # the seed table of the shipped general seed script (gate only)
        "ref": "kite-threshold-reference-m3sq3.json",        # the independent AMFlow reference at t = -2, -7/3 (gate only)
        "poles": lambda mp: [mp.mpf(0), mp.mpf(1), mp.mpf(9), mp.mpf(25), mp.mpf(297)/8,
                             4-2*mp.sqrt(3), 4+2*mp.sqrt(3), 12-6*mp.sqrt(3), 12+6*mp.sqrt(3)],
        "rho": lambda mp: 4 - 2*mp.sqrt(3),
        "poles_text": "s = 0, 1, 9, 25, 297/8, 4 -+ 2 sqrt3, 12 -+ 6 sqrt3",
        "bar": 98,            # floor of the independent gate of record (98.74 digits)
        "seed_table_bar": 50,  # the live seed vs the shipped 55-digit table
        "quick_point": "-2",
        "ref_points": ["-2", "-7/3"],
    },
}
# sha256 of the shipped data files of the 1,4,9,3,0 line and of the --AB-ring
# pins file (2026-09-06) (computed by the vendoring producers, never typed); a
# mismatch is REFUSED by name before any value is printed.
PINS = {
    "kite-threshold-connection-m3sq3.json": "39b7653e092c24af06caa5d1bc29a39261d66261d78fc716d4be1b87558355f5",
    "kite-threshold-seed-m3sq3.json": "7585d3f6568b9710fafb9efd7d1bbff8fd9268aa82cc0694af0dc9f50c14e293",
    "kite-threshold-reference-m3sq3.json": "079cc08faa72523591bc94ddd94b2a4c617603d07dae2fbebc1d0b869d4f6753",
    "kite-threshold-AB-ring.json": "8fb97a83dc244ed9aab66777ceece7c18f8df38afe46b499faf8c9ca0ee99b78",
}


def _select_masses():
    """The mass line: --masses a,b,c,d,e on the command line (a run of this
    file) or KITE_THRESHOLD_MASSES=a,b,c,d,e in the environment (library
    use); default 1,4,9,2,0.  Any other five-tuple is refused by name (exit 2)."""
    val = os.environ.get("KITE_THRESHOLD_MASSES")
    if __name__ == "__main__":
        argv = sys.argv[1:]
        for i, a in enumerate(argv):
            if a == "--masses" and i + 1 < len(argv):
                val = argv[i + 1]
            elif a.startswith("--masses="):
                val = a.split("=", 1)[1]
    if val is None:
        return (1, 4, 9, 2, 0)
    try:
        t = tuple(int(x) for x in val.replace(" ", "").split(","))
    except ValueError:
        t = None
    if t not in MASS_CONFIGS:
        sys.stderr.write(f"kite-threshold-evaluate: error: unsupported --masses {val!r}: five squared "
                         "masses M1,M2,M3,M4,M5 of (D1,D2,D3,D4,D5) are named, the fifth massless; the "
                         "shipped lines are 1,4,9,2,0 (the default) and 1,4,9,3,0\n")
        raise SystemExit(2)
    return t


MASSES = _select_masses()
CFG = MASS_CONFIGS[MASSES]
M3SQ3 = MASSES == (1, 4, 9, 3, 0)


def _pinned_path(name):
    """Path of a shipped file; a PINNED file (the 1,4,9,3,0 data) is refused on
    any byte change (exit 3, the recorded and recomputed sha256 named)."""
    path = os.path.join(HERE, name)
    want = PINS.get(name)
    if want is not None:
        import hashlib
        if not os.path.exists(path):
            sys.stderr.write(f"kite-threshold-evaluate REFUSED: pinned file {name} is missing (recorded "
                             f"sha256 {want}); not serving --masses {','.join(map(str, MASSES))}\n")
            raise SystemExit(3)
        have = hashlib.sha256(open(path, "rb").read()).hexdigest()
        if have != want:
            pos = next((k + 1 for k, (x, y) in enumerate(zip(want, have)) if x != y), 0)
            sys.stderr.write(f"kite-threshold-evaluate REFUSED: {name} integrity pin mismatch (recorded {want}, "
                             f"recomputed {have}; first differing hex position {pos} of 64, 1-based) -- "
                             f"the shipped file was altered; not serving --masses "
                             f"{','.join(map(str, MASSES))}\n")
            raise SystemExit(3)
    return path


CONN = _pinned_path(CFG["conn"])     # the selected line's connection (pin-checked at the new line)

GATE_POINTS = ["-1", "-3", "-4", "-5", "-6", "-8"]

# ---- independent AMFlow oracle (2026-07-06) ----
# GATE-CACHE ONLY: these strings are the AMFlow evaluations of the original
# computation (eps^0 of the (1,1,1,1,1) master, ball radii <= 8.6e-112); the
# recompute path above (classical seed + Frobenius + certified transport) is
# the DEFINITION.  Certified held-out digits per point = the cross-validated
# table of the paper's threshold-kite results (min of the AMFlow claim and
# measured agreement), floored.  Never used in the construction.
ORACLE = {
    "-1": ("-0.27540984477021447342068745859691310588860868014821576481576153141273755135500178024919653971537412561053242600", 110),
    "-2": ("-0.26163647668720164683846920770514847024364853765347239468892479468578483793928756889315649614604063650173533176", 109),
    "-3": ("-0.24936351046159947264821124527943838329031868869202716155331505505113015461424873439034886286533848276787174341", 109),
    "-4": ("-0.23833892398988994579954236214150217858905812950771020542766798236538201180801995588120001212602121395131772499", 97),
    "-5": ("-0.22836711115982498192615806479220118894936537413593748131687728450676017271325561110933701245395288712685891025", 97),
    "-6": ("-0.21929335268425407528116892029837299085000521518664828776171548585004310064895676329056835111670958474017336271", 97),
    "-8": ("-0.20336554998081175260272672394791130203414552863889470547737424958186722272761379465710445337192989695247127842", 81),
}
if M3SQ3:
    # the table above is the 1,4,9,2,0 line's; the 1,4,9,3,0 line's gate is the
    # 22-entry reference table (m3sq3_reference_table), so run() finds no
    # top-only oracle entry at the new line's points.
    ORACLE = {}


# ============================ eps-Laurent series over mpf ============================
def make_series_tools(mp):
    class Ser:
        __slots__ = ("k0", "c")
        def __init__(self, k0, c): self.k0 = k0; self.c = list(c)
        @staticmethod
        def const(v, n): return Ser(0, [mp.mpf(v)] + [mp.mpf(0)]*(n-1))
        def __mul__(self, o):
            if isinstance(o, Ser):
                n = min(len(self.c), len(o.c))
                out = [mp.mpf(0)]*n
                for i in range(n):
                    ai = self.c[i]
                    if ai == 0: continue
                    for j in range(n-i): out[i+j] += ai*o.c[j]
                return Ser(self.k0+o.k0, out)
            return Ser(self.k0, [x*o for x in self.c])
        __rmul__ = __mul__
        def __add__(self, o):
            if not isinstance(o, Ser): o = Ser.const(o, len(self.c))
            k0 = min(self.k0, o.k0)
            n = min(self.k0+len(self.c), o.k0+len(o.c)) - k0
            out = [mp.mpf(0)]*n
            for i, v in enumerate(self.c):
                j = self.k0-k0+i
                if j < n: out[j] += v
            for i, v in enumerate(o.c):
                j = o.k0-k0+i
                if j < n: out[j] += v
            return Ser(k0, out)
        def __sub__(self, o): return self + o*(-1) if isinstance(o, Ser) else self + (-o)
        def inv(self):
            a0 = self.c[0]; n = len(self.c)
            out = [mp.mpf(0)]*n; out[0] = 1/a0
            for k in range(1, n):
                s_ = mp.mpf(0)
                for j in range(1, k+1): s_ += self.c[j]*out[k-j]
                out[k] = -s_/a0
            return Ser(-self.k0, out)
        def coeff(self, k):
            j = k - self.k0
            return self.c[j] if 0 <= j < len(self.c) else mp.mpf(0)

    def ser_exp(s):
        assert s.k0 >= 0
        n = len(s.c)
        g = [mp.mpf(0)]*n
        for i, v in enumerate(s.c):
            if s.k0+i < n: g[s.k0+i] = v
        out = [mp.mpf(0)]*n
        out[0] = mp.exp(g[0]); g0 = g[0]; g[0] = mp.mpf(0)
        for k in range(1, n):
            s_ = mp.mpf(0)
            for j in range(1, k+1): s_ += j*g[j]*out[k-j]
            out[k] = s_/k
        return Ser(0, out)

    def pow_ser(base, expo): return ser_exp(expo * mp.log(base))

    def gamma_ser(x0, coef, n):
        x0 = mp.mpf(x0); m = 0
        while x0 + m < mp.mpf('0.5'): m += 1
        lg = [mp.loggamma(x0+m)]; fact = mp.mpf(1)
        for k in range(1, n):
            fact *= k
            lg.append(mp.polygamma(k-1, x0+m)*mp.mpf(coef)**k/fact)
        num = ser_exp(Ser(0, lg))
        den = Ser.const(1, n)
        for j in range(m):
            z0 = x0 + j
            if z0 == 0: den = den*Ser(1, [mp.mpf(coef)] + [mp.mpf(0)]*(n-1))
            else:       den = den*Ser(0, [z0, mp.mpf(coef)] + [mp.mpf(0)]*(n-2))
        return num*den.inv()
    return Ser, ser_exp, pow_ser, gamma_ser


# ============================ classical p^2=0 seed ============================
def seed_vector(mp, n, want_blocks=False):
    Ser, ser_exp, pow_ser, gamma_ser = make_series_tools(mp)
    lin = lambda c0, c1: Ser(0, [mp.mpf(c0), mp.mpf(c1)] + [mp.mpf(0)]*(n-2))

    def T1(msq):
        return gamma_ser(-1, 1, n)*pow_ser(mp.mpf(msq), lin(1, -1))*(-1)

    def hyp2f1_eps(r):
        r = mp.mpf(r)
        out = [mp.mpf(0)]*n; out[0] = mp.mpf(1)
        term = Ser.const(1, n)
        tol = mp.mpf(10)**(-mp.mp.dps-5); k = 1
        while True:
            term = term*lin(k-1, 1)*lin(k+1, -1).inv()*r
            mx = mp.mpf(0)
            for i, v in enumerate(term.c):
                if term.k0+i < n:
                    out[term.k0+i] += v
                    mx = max(mx, abs(v))
            if mx < tol and k > 3: break
            k += 1
        return Ser(0, out)

    def T(a, b):
        a = mp.mpf(a); b = mp.mpf(b)
        if b > a: a, b = b, a
        r = b/a
        pre = gamma_ser(0, 1, n)*gamma_ser(1, -1, n)*lin(1, -1).inv()*pow_ser(a, lin(1, -2))
        t1 = gamma_ser(-1, 2, n)*pow_ser(1-r, lin(1, -2))
        t2 = pow_ser(r, lin(1, -1))*gamma_ser(0, 1, n)*gamma_ser(2, -1, n).inv()*hyp2f1_eps(r)
        return pre*(t1 - t2)*(-1)   # Minkowski AMFlow-bare = -T_E

    t1 = {m: T1(m) for m in (1, 4, 9, 2)}
    TS = {p: T(*p) for p in ((1, 9), (4, 9), (1, 2), (4, 2))}
    th = mp.mpf(1)/3; sv = mp.mpf(1)/7
    S = {
        (1,0,1,0,0): t1[1]*t1[9],
        (0,1,1,0,0): t1[4]*t1[9],
        (1,1,1,0,0): (t1[4] + t1[1]*(-1))*t1[9]*th,
        (1,0,0,1,0): t1[1]*t1[2],
        (1,0,1,1,0): t1[1]*(t1[9] + t1[2]*(-1))*sv,
        (0,1,0,1,0): t1[4]*t1[2],
        (1,1,0,1,0): (t1[4] + t1[1]*(-1))*t1[2]*th,
        (0,1,1,1,0): t1[4]*(t1[9] + t1[2]*(-1))*sv,
        (1,1,1,1,0): (t1[4] + t1[1]*(-1))*(t1[9] + t1[2]*(-1))*th*sv,
        (1,0,1,0,1): TS[(1,9)],
        (0,1,1,0,1): TS[(4,9)],
        (-1,1,1,0,1): TS[(4,9)]*3,
        (0,1,1,-1,1): TS[(4,9)]*7,
        (1,1,1,0,1): (TS[(4,9)] + TS[(1,9)]*(-1))*th,
        (1,0,0,1,1): TS[(1,2)],
        (1,0,-1,1,1): TS[(1,2)]*(-7),
        (1,-1,0,1,1): TS[(1,2)]*(-3),
        (1,0,1,1,1): (TS[(1,9)] + TS[(1,2)]*(-1))*sv,
        (0,1,0,1,1): TS[(4,2)],
        (1,1,0,1,1): (TS[(4,2)] + TS[(1,2)]*(-1))*th,
        (0,1,1,1,1): (TS[(4,9)] + TS[(4,2)]*(-1))*sv,
        (1,1,1,1,1): (TS[(4,9)] + TS[(4,2)]*(-1) + TS[(1,9)]*(-1) + TS[(1,2)])*th*sv,
    }
    if want_blocks:
        return S, {"T1": t1, "TS": TS}
    return S


def seed_integrity_gate(mp, dps, quiet=False):
    """2026-07-06: independent compare of the classical seed blocks.

    Recomputes every T1/T eps-series through the SAME code path as the live
    seed (seed_vector -> make_series_tools) at working precision dps+40 and
    n=16 eps-orders, evaluates the series at the exact numeric point
    eps0 = 10^-9, and compares against a fully independent direct evaluation
    (mp.gamma / mp.power / mp.hyp2f1 -- none of the hand-rolled series code).

    Certified truncation: every ingredient's eps-series has radius of
    convergence >= rho_eps = 1/2 (nearest nonzero eps-pole over all factors:
    Gamma(2eps-1) at eps = 1/2; Gamma(-1+eps), Gamma(eps) poles at 0 are the
    explicit Laurent heads k0; Gamma(1-eps), 1/(1-eps), 1/Gamma(2-eps),
    2F1(eps,1;2-eps;r) with 0<r<1 are analytic in |eps|<1), so the term ratio
    at eps0 is certified r = eps0/rho_eps and
        tail <= max(|c_n eps0^(k0+n)| over the last TAIL_WINDOW terms) * r/(1-r)
    (same trailing-window pattern as the transport gate).  Refine-until-bound:
    tail must sit below tol_sig*1e-6 or n doubles (cap 16x seed, RAISE at cap).

    Tolerance tol_sig = 10^-(dps+10): any mutation of the seed construction
    with relative effect >= ~10^-(dps+10) mismatches the direct values and
    raises (rc != 0) -- at ANY working dps, because the compare runs at
    dps+40.  The ker A_{-1} residual check CANNOT do this job: the t2 (2F1)
    part of T lies in ker A_{-1} separately (measured: a 1e-30 t2 mutation
    leaves the kernel residual at 6.4e-62).
    """
    SIG_GUARD = 40
    with mp.workdps(dps + SIG_GUARD):
        tol_sig = mp.mpf(10)**(-(dps + 10))
        eps0 = mp.mpf(10)**(-9)
        rho_eps = mp.mpf(1)/2
        r = eps0/rho_eps
        rfac = r/(1 - r)
        TAIL_WINDOW = 8
        n0 = 16
        NCAP = 16*n0

        # independent direct evaluations at eps = eps0 (no series code)
        def T1_direct(msq):
            return -mp.gamma(-1 + eps0)*mp.mpf(msq)**(1 - eps0)

        def T_direct(a, b):
            a = mp.mpf(a); b = mp.mpf(b)
            if b > a: a, b = b, a
            rr = b/a
            pre = mp.gamma(eps0)*mp.gamma(1 - eps0)/(1 - eps0)*a**(1 - 2*eps0)
            t1_ = mp.gamma(2*eps0 - 1)*(1 - rr)**(1 - 2*eps0)
            t2_ = rr**(1 - eps0)*mp.gamma(eps0)/mp.gamma(2 - eps0) \
                  * mp.hyp2f1(eps0, 1, 2 - eps0, rr)
            return -(pre*(t1_ - t2_))

        direct = {("T1", m): T1_direct(m) for m in (1, 4, 9, 2)}
        direct.update({("TS", p): T_direct(*p)
                       for p in ((1, 9), (4, 9), (1, 2), (4, 2))})

        n = n0
        while True:
            _, blocks = seed_vector(mp, n, want_blocks=True)   # SAME code path
            worst_tail = mp.mpf(0)
            for kind in ("T1", "TS"):
                for key, ser in blocks[kind].items():
                    tail = mp.mpf(0)
                    for i in range(len(ser.c) - TAIL_WINDOW, len(ser.c)):
                        t_ = abs(ser.c[i])*eps0**(ser.k0 + i)
                        if t_ > tail: tail = t_
                    tail *= rfac
                    if tail > worst_tail: worst_tail = tail
            if worst_tail < tol_sig*mp.mpf(10)**(-6):
                break
            if n >= NCAP:
                raise RuntimeError(
                    "kite-threshold seed gate: certified eps-series tail NOT met: "
                    f"tail {mp.nstr(worst_tail, 5)} >= "
                    f"{mp.nstr(tol_sig*mp.mpf(10)**(-6), 5)} at n = {n} "
                    f"(cap {NCAP} = 16 x initial {n0})")
            n = min(2*n, NCAP)

        worst = mp.mpf(0); worst_name = None
        for kind in ("T1", "TS"):
            for key, ser in blocks[kind].items():
                val = mp.mpf(0)
                for i, c in enumerate(ser.c):
                    val += c*eps0**(ser.k0 + i)
                dv = direct[(kind, key)]
                mism = abs(val - dv)/abs(dv)
                if mism > worst:
                    worst = mism; worst_name = f"{kind}{key}"
        if worst >= tol_sig:
            raise RuntimeError(
                "kite-threshold seed gate: INDEPENDENT COMPARE FAILED at block "
                f"{worst_name}: series-vs-direct relative mismatch "
                f"{mp.nstr(worst, 5)} >= tol {mp.nstr(tol_sig, 5)} at "
                f"eps = 1e-9, dps+40 = {dps + SIG_GUARD} "
                "(seed construction mutated or broken -- fail-closed)")
        if not quiet:
            print(f"[seed-gate] independent Gamma/2F1 compare at eps=1e-9, "
                  f"dps+40: worst rel mismatch {mp.nstr(worst, 3)} "
                  f"< tol {mp.nstr(tol_sig, 3)} "
                  f"(certified tail {mp.nstr(worst_tail, 3)}, n = {n}) OK")


# ============================ the seed at general masses (2026-09-05) ============================
def seed_vector_general(mp, n, masses, want_blocks=False):
    """The classical p^2=0 seed of seed_vector with the squared masses
    (M1, M2, M3, M4) of (D1, D2, D3, D4) as parameters (D5 massless):
    partial fractions 1/(D1 D2) = (1/D2 - 1/D1)/(M2 - M1),
    1/(D3 D4) = (1/D3 - 1/D4)/(M3 - M4); numerators D1 = D2 + (M2 - M1),
    D4 = D3 + (M3 - M4), D3 = D4 - (M3 - M4), D2 = D1 - (M2 - M1), then the
    scaleless drop; sunset pairs (M1,M3), (M2,M3), (M1,M4), (M2,M4).  The
    series code (T1, 2F1, T) is seed_vector's line for line; the 22-entry
    table is seed_vector's with 3 -> M2 - M1, 7 -> M3 - M4, 1/3 -> 1/(M2 - M1),
    1/7 -> 1/(M3 - M4).  At (1, 4, 9, 2) the result is seed_vector's bit for
    bit (m3sq3_seed_gate asserts this at every new-line start)."""
    Ser, ser_exp, pow_ser, gamma_ser = make_series_tools(mp)
    lin = lambda c0, c1: Ser(0, [mp.mpf(c0), mp.mpf(c1)] + [mp.mpf(0)]*(n-2))

    def T1(msq):
        return gamma_ser(-1, 1, n)*pow_ser(mp.mpf(msq), lin(1, -1))*(-1)

    def hyp2f1_eps(r):
        r = mp.mpf(r)
        out = [mp.mpf(0)]*n; out[0] = mp.mpf(1)
        term = Ser.const(1, n)
        tol = mp.mpf(10)**(-mp.mp.dps-5); k = 1
        while True:
            term = term*lin(k-1, 1)*lin(k+1, -1).inv()*r
            mx = mp.mpf(0)
            for i, v in enumerate(term.c):
                if term.k0+i < n:
                    out[term.k0+i] += v
                    mx = max(mx, abs(v))
            if mx < tol and k > 3: break
            k += 1
        return Ser(0, out)

    def T(a, b):
        a = mp.mpf(a); b = mp.mpf(b)
        if b > a: a, b = b, a
        r = b/a
        pre = gamma_ser(0, 1, n)*gamma_ser(1, -1, n)*lin(1, -1).inv()*pow_ser(a, lin(1, -2))
        t1 = gamma_ser(-1, 2, n)*pow_ser(1-r, lin(1, -2))
        t2 = pow_ser(r, lin(1, -1))*gamma_ser(0, 1, n)*gamma_ser(2, -1, n).inv()*hyp2f1_eps(r)
        return pre*(t1 - t2)*(-1)   # Minkowski AMFlow-bare = -T_E

    m1, m2, M, m3 = masses
    assert m2 != m1 and M != m3, "partial fractions need M2 != M1 and M3 != M4"
    for (x, y) in ((m1, M), (m2, M), (m1, m3), (m2, m3)):
        assert x != y, f"sunset pair ({x},{y}) needs distinct masses (T has r = b/a < 1)"
    t1 = {m: T1(m) for m in (m1, m2, M, m3)}
    TS = {p: T(*p) for p in ((m1, M), (m2, M), (m1, m3), (m2, m3))}
    d12 = m2 - m1; d34 = M - m3
    th = mp.mpf(1)/d12; sv = mp.mpf(1)/d34
    S = {
        (1,0,1,0,0): t1[m1]*t1[M],
        (0,1,1,0,0): t1[m2]*t1[M],
        (1,1,1,0,0): (t1[m2] + t1[m1]*(-1))*t1[M]*th,
        (1,0,0,1,0): t1[m1]*t1[m3],
        (1,0,1,1,0): t1[m1]*(t1[M] + t1[m3]*(-1))*sv,
        (0,1,0,1,0): t1[m2]*t1[m3],
        (1,1,0,1,0): (t1[m2] + t1[m1]*(-1))*t1[m3]*th,
        (0,1,1,1,0): t1[m2]*(t1[M] + t1[m3]*(-1))*sv,
        (1,1,1,1,0): (t1[m2] + t1[m1]*(-1))*(t1[M] + t1[m3]*(-1))*th*sv,
        (1,0,1,0,1): TS[(m1,M)],
        (0,1,1,0,1): TS[(m2,M)],
        (-1,1,1,0,1): TS[(m2,M)]*d12,
        (0,1,1,-1,1): TS[(m2,M)]*d34,
        (1,1,1,0,1): (TS[(m2,M)] + TS[(m1,M)]*(-1))*th,
        (1,0,0,1,1): TS[(m1,m3)],
        (1,0,-1,1,1): TS[(m1,m3)]*(-d34),
        (1,-1,0,1,1): TS[(m1,m3)]*(-d12),
        (1,0,1,1,1): (TS[(m1,M)] + TS[(m1,m3)]*(-1))*sv,
        (0,1,0,1,1): TS[(m2,m3)],
        (1,1,0,1,1): (TS[(m2,m3)] + TS[(m1,m3)]*(-1))*th,
        (0,1,1,1,1): (TS[(m2,M)] + TS[(m2,m3)]*(-1))*sv,
        (1,1,1,1,1): (TS[(m2,M)] + TS[(m2,m3)]*(-1) + TS[(m1,M)]*(-1) + TS[(m1,m3)])*th*sv,
    }
    if want_blocks:
        return S, {"T1": t1, "TS": TS}
    return S


def seed_integrity_gate_general(mp, dps, masses, quiet=False):
    """seed_integrity_gate with the masses as parameters: the same independent
    direct Gamma/2F1 evaluations at eps = 1e-9, dps+40, against the SAME series
    code path (seed_vector_general -> make_series_tools); the same certified
    tail, tolerance and RAISE."""
    SIG_GUARD = 40
    m1, m2, M, m3 = masses
    with mp.workdps(dps + SIG_GUARD):
        tol_sig = mp.mpf(10)**(-(dps + 10))
        eps0 = mp.mpf(10)**(-9)
        rho_eps = mp.mpf(1)/2
        r = eps0/rho_eps
        rfac = r/(1 - r)
        TAIL_WINDOW = 8
        n0 = 16
        NCAP = 16*n0

        def T1_direct(msq):
            return -mp.gamma(-1 + eps0)*mp.mpf(msq)**(1 - eps0)

        def T_direct(a, b):
            a = mp.mpf(a); b = mp.mpf(b)
            if b > a: a, b = b, a
            rr = b/a
            pre = mp.gamma(eps0)*mp.gamma(1 - eps0)/(1 - eps0)*a**(1 - 2*eps0)
            t1_ = mp.gamma(2*eps0 - 1)*(1 - rr)**(1 - 2*eps0)
            t2_ = rr**(1 - eps0)*mp.gamma(eps0)/mp.gamma(2 - eps0) \
                  * mp.hyp2f1(eps0, 1, 2 - eps0, rr)
            return -(pre*(t1_ - t2_))

        direct = {("T1", m): T1_direct(m) for m in (m1, m2, M, m3)}
        direct.update({("TS", p): T_direct(*p)
                       for p in ((m1, M), (m2, M), (m1, m3), (m2, m3))})

        n = n0
        while True:
            _, blocks = seed_vector_general(mp, n, masses, want_blocks=True)   # SAME code path
            worst_tail = mp.mpf(0)
            for kind in ("T1", "TS"):
                for key, ser in blocks[kind].items():
                    tail = mp.mpf(0)
                    for i in range(len(ser.c) - TAIL_WINDOW, len(ser.c)):
                        t_ = abs(ser.c[i])*eps0**(ser.k0 + i)
                        if t_ > tail: tail = t_
                    tail *= rfac
                    if tail > worst_tail: worst_tail = tail
            if worst_tail < tol_sig*mp.mpf(10)**(-6):
                break
            if n >= NCAP:
                raise RuntimeError(
                    "kite-threshold seed gate: certified eps-series tail NOT met: "
                    f"tail {mp.nstr(worst_tail, 5)} >= "
                    f"{mp.nstr(tol_sig*mp.mpf(10)**(-6), 5)} at n = {n} "
                    f"(cap {NCAP} = 16 x initial {n0})")
            n = min(2*n, NCAP)

        worst = mp.mpf(0); worst_name = None
        for kind in ("T1", "TS"):
            for key, ser in blocks[kind].items():
                val = mp.mpf(0)
                for i, c in enumerate(ser.c):
                    val += c*eps0**(ser.k0 + i)
                dv = direct[(kind, key)]
                mism = abs(val - dv)/abs(dv)
                if mism > worst:
                    worst = mism; worst_name = f"{kind}{key}"
        if worst >= tol_sig:
            raise RuntimeError(
                "kite-threshold seed gate: INDEPENDENT COMPARE FAILED at block "
                f"{worst_name}: series-vs-direct relative mismatch "
                f"{mp.nstr(worst, 5)} >= tol {mp.nstr(tol_sig, 5)} at "
                f"eps = 1e-9, dps+40 = {dps + SIG_GUARD} "
                "(seed construction mutated or broken -- fail-closed)")
        if not quiet:
            print(f"[seed-gate] independent Gamma/2F1 compare at eps=1e-9, "
                  f"dps+40: worst rel mismatch {mp.nstr(worst, 3)} "
                  f"< tol {mp.nstr(tol_sig, 3)} "
                  f"(certified tail {mp.nstr(worst_tail, 3)}, n = {n}) OK")


def m3sq3_seed_gate(mp, dps):
    """The two seed controls of the 1,4,9,3,0 line, run before any transport
    (RAISING).  (1) seed_vector_general at the DEFAULT masses (1,4,9,2) must
    reproduce seed_vector bit for bit, all 66 eps^-2..eps^0 coefficients (the
    twin is the served seed).  (2) the live seed at (1,4,9,3) vs the shipped
    table kite-threshold-seed-m3sq3.json (computed independently by the shipped
    mass-general seed script from the same classical formulas): every
    coefficient >= seed_table_bar digits (relative; the two analytically-zero
    entries of the finite top master compared absolutely at 10^-50)."""
    tab = json.load(open(_pinned_path(CFG["seed_table"])))
    bar = CFG["seed_table_bar"]
    with mp.workdps(max(dps, int(tab["dps"])) + 10):
        S_def = seed_vector(mp, 8)
        S_gen = seed_vector_general(mp, 8, (1, 4, 9, 2))
        n_bit = 0
        for m_ in S_def:
            for K in (-2, -1, 0):
                if S_def[m_].coeff(K) != S_gen[m_].coeff(K):
                    raise RuntimeError(
                        f"kite-threshold general seed CONTROL FAILED: seed_vector_general at the default "
                        f"masses differs from seed_vector at master {m_} eps^{K} -- the twin is not the served seed")
                n_bit += 1
        S3 = seed_vector_general(mp, 8, MASSES[:4])
        worst = None; worst_name = None; n_abs = 0
        for m_str, cols in tab["table"].items():
            m_ = tuple(int(x) for x in m_str.strip("()").split(","))
            for K in (-2, -1, 0):
                ref = mp.mpf(cols[f"eps^{K}"]); val = S3[m_].coeff(K)
                if abs(ref) < mp.mpf(10)**(-40):
                    n_abs += 1
                    if not abs(val) < mp.mpf(10)**(-50):
                        raise RuntimeError(
                            f"kite-threshold seed-table gate FAILED at master {m_} eps^{K}: analytically-zero "
                            f"entry has |live| = {mp.nstr(abs(val), 3)} >= 1e-50 -- seed construction broken")
                    continue
                dd = -mp.log10(abs(val - ref)/abs(ref)) if val != ref else mp.mpf(int(tab["dps"]))
                if worst is None or dd < worst:
                    worst = dd; worst_name = f"{m_} eps^{K}"
        if not worst >= bar:
            raise RuntimeError(
                f"kite-threshold seed-table gate FAILED: live seed at masses {MASSES[:4]} vs the shipped table "
                f"{CFG['seed_table']} worst agreement {mp.nstr(worst, 4)} d at {worst_name} < {bar} -- "
                f"{CFG['seed_table']} corrupted or the seed construction broken; value NOT computed")
        print(f"[seed] general seed at the default masses (1,4,9,2,0) == seed_vector bit for bit ({n_bit} coefficients) OK")
        print(f"[seed] live seed at masses {','.join(map(str, MASSES))} vs the shipped table {CFG['seed_table']} "
              f"({tab['dps']} d, gate only): worst {mp.nstr(worst, 4)} d at {worst_name} vs >= {bar} "
              f"({n_abs} analytically-zero entries < 1e-50) OK")


def m3sq3_reference_table(mp, rec, s_str, dps):
    """The 1,4,9,3,0 line at a reference point: print every (master, eps order)
    entry of the shipped independent reference with the agreement recomputed
    here (relative, at max(dps, 130)+10 working digits, the oracle gate's
    form) and RAISE unless every digit-compared entry reaches
    min(bar, dps-8) digits and every rational entry equals its rational
    within 10^-(dps-8).  Returns (worst_digits, n_digit_compared, n_rational)
    or None when the point carries no reference."""
    from fractions import Fraction
    ref = json.load(open(_pinned_path(CFG["ref"])))
    blk = None
    for k, v in ref["points"].items():
        if Fraction(k) == Fraction(s_str):
            blk = v; k_ref = k
    if blk is None:
        print(f"  (no shipped reference at t={s_str}; the reference points of this line are "
              f"t = {', '.join(ref['points'].keys())})")
        return None
    y, IDX, MASTERS = rec["y"], rec["IDX"], rec["MASTERS"]
    bar = min(CFG["bar"], dps - 8)
    ents = blk["entries"]
    print(f"  independent reference at t={k_ref} (AMFlow, gate only): "
          f"{'entry':>24} | {'computed here':>44} | {'reference':>44} | agreement")
    worst, n_dig, n_rat, bad = None, 0, 0, []
    for i_str in sorted(ents, key=int):
        for K_str in sorted(ents[i_str], key=int):
            i, K = int(i_str), int(K_str)
            e = ents[i_str][K_str]
            comp = y[IDX[(i, K)]]
            label = f"M{i:<2d}{str(MASTERS[i]):<17s} eps^{K:>2d}"
            with mp.workdps(max(dps, 130) + 10):
                rv = mp.mpf(e["mid"])
                ref_40 = mp.nstr(rv, 40)
                if "rational" in e:
                    n_rat += 1
                    q = Fraction(e["rational"])
                    qv = mp.mpf(q.numerator)/q.denominator
                    resid = abs(comp - qv)
                    ok = resid <= mp.mpf(10)**(-(dps - 8))*max(mp.mpf(1), abs(qv))
                    verdict = (f"exact (= {e['rational']} within 1e-{dps - 8}; |residual| {mp.nstr(resid, 3)})"
                               if ok else f"NOT exact (= {e['rational']}? |residual| {mp.nstr(resid, 3)})")
                else:
                    n_dig += 1
                    dvv = abs((comp - rv)/rv)
                    d = -mp.log10(dvv) if dvv > 0 else mp.mpf(dps + e["sig_digits"])
                    worst = d if worst is None else min(worst, d)
                    ok = d >= bar
                    verdict = f"{mp.nstr(d, 5):>6s} d {'>=' if ok else '< '} {bar}"
                if not ok:
                    bad.append(label)
            print(f"  {label} | {mp.nstr(comp, 40):>44} | {ref_40:>44} | {verdict}")
    print(f"[gate] masses {','.join(map(str, MASSES))} reference at t={k_ref}: {n_dig} digit-compared entries worst "
          f"{mp.nstr(worst, 5)} d vs bar >= {bar} (= min({CFG['bar']}, dps-8)); {n_rat} rational "
          f"entries exact -> {'PASS' if not bad else 'FAIL'}")
    if bad:
        raise RuntimeError(
            f"kite-threshold reference gate FAILED at t={k_ref}: {len(bad)} of {n_dig + n_rat} "
            f"entries below the bar or not exact ({', '.join(bad[:4])}{' ...' if len(bad) > 4 else ''}) "
            f"-- value NOT certified")
    return worst, n_dig, n_rat


def _m3sq3_two_precision(mp, rec, rec2, s_str, dps):
    """--check at the 1,4,9,3,0 line: the 22 reference entries of the state at
    dps vs the dps+60 rerun (relative; RAISING below dps-8)."""
    from fractions import Fraction
    ref = json.load(open(_pinned_path(CFG["ref"])))
    blk = next((v for k, v in ref["points"].items() if Fraction(k) == Fraction(s_str)), None)
    if blk is None:
        return None
    worst = None
    with mp.workdps(dps + 70):
        for i_str, cols in blk["entries"].items():
            for K_str in cols:
                i, K = int(i_str), int(K_str)
                a_, b_ = rec["y"][rec["IDX"][(i, K)]], rec2["y"][rec2["IDX"][(i, K)]]
                if b_ == 0:
                    continue
                dd = -mp.log10(abs((a_-b_)/b_)) if a_ != b_ else mp.mpf(dps + 60)
                worst = dd if worst is None else min(worst, dd)
    stat = "OK" if worst >= dps - 8 else "FAIL"
    print(f"  t={s_str}: 22-entry two-precision agreement worst {mp.nstr(worst, 5)} digits vs >= {dps - 8}  [{stat}]")
    return stat == "OK"


# ============================ transport machinery ============================
def taylor_shift(p, x0, mp):
    a = [mp.mpf(c) for c in p]
    out = []
    while a:
        rem = mp.mpf(0)
        for c in reversed(a): rem = rem*x0 + c
        out.append(rem)
        q = [mp.mpf(0)]*(len(a)-1); carry = mp.mpf(0)
        for idx in range(len(a)-1, 0, -1):
            carry = a[idx] + carry*x0
            q[idx-1] = carry
        a = q
    return out

def series_div(P, Q, order, mp):
    out = [mp.mpf(0)]*order; q0 = Q[0]
    for k in range(order):
        s_ = P[k] if k < len(P) else mp.mpf(0)
        for j in range(1, min(k, len(Q)-1)+1): s_ -= Q[j]*out[k-j]
        out[k] = s_/q0
    return out


def run(dps, points, order=None, s1=None, want_state=False, quiet=False,
        point_only=False):
    import mpmath as mp
    mp.mp.dps = dps
    C = json.load(open(CONN))
    MASTERS = [tuple(m) for m in C["masters"]]
    N = len(MASTERS); TOP = C["TOP_idx"]
    KMIN, KMAX = -2, 0
    ORDERS = list(range(KMIN, KMAX+1))
    STATE = [(i, K) for i in range(N) for K in ORDERS]
    IDX = {sK: k for k, sK in enumerate(STATE)}
    NS = len(STATE)
    # 2026-07-05: `order` (and Nser below) are STARTING seeds only —
    # every truncation is certified at runtime by the trailing-window gates
    # below and grown by exact continuation until the bound passes.  Seed sized
    # from the MEASURED worst step (x0=-6, h=-1.8): first-try bound(N) =
    # 10^(1.55-0.832N) (fit N=74/148, validated at N=60), so N = 1.25*dps+24
    # keeps the worst first-try bound below tol/10^6 (measured calibration
    # of the original computation).  The old seed
    # 0.9*dps+20 delivered only ~dps digits on that step (under-converged).
    order = order or max(60, int(1.25*dps) + 24)
    s1 = mp.mpf(s1 if s1 is not None else "-0.02")

    # ---- fail-closed domain guard (2026-07-06) ----
    # The transport below steps strictly DOWNWARD from s1 (h = -min(...)):
    # a target above s1 is unreachable and previously spun the step loop
    # forever (measured rc=124 under a 60 s timeout at t = -19/1000).
    # Points are parsed HERE, before any heavy work, so the refusal is fast.
    from fractions import Fraction
    tvals = sorted([mp.mpf(Fraction(p).numerator)/mp.mpf(Fraction(p).denominator)
                    for p in points], reverse=True)
    bad = [t for t in tvals if t > s1]
    if bad:
        raise DomainRefusal(
            "kite-threshold DOMAIN REFUSAL: target(s) "
            + ", ".join("t = " + mp.nstr(t, 10) for t in bad)
            + f" lie ABOVE the transport start s1 = {mp.nstr(s1, 10)}."
            "  The vacuum-seeded transport walks strictly downward from s1 on the"
            " Euclidean axis, so t > s1 is unreachable (previously an"
            " infinite step loop).  Requested points must satisfy t <= s1.")

    # ---- certified-tail gate parameters (2026-07-05) ----
    TAIL_WINDOW = 8      # trailing terms entering the envelope max
    TAIL_GUARD = 10      # tol = 10^-(dps+TAIL_GUARD); calibrated 7/05 (ROW_REPORT):
    #   worst measured transport-step bound and Frobenius bound sit well below
    #   tol on the default gate set => healthy runs never escalate
    NCAP_FAC = 16        # hard cap = NCAP_FAC x starting order (growth by doubling)
    tol = mp.mpf(10)**(-(dps + TAIL_GUARD))

    ENT = []
    for e in C["entries"]:
        ENT.append((e["i"], e["j"],
                    [mp.mpf(c)/e["P0den"] for c in e["P0"]],
                    [mp.mpf(c)/e["P1den"] for c in e["P1"]],
                    [mp.mpf(c)/e["Qden"] for c in e["Q"]]))

    # seed (the selected line's masses; the default call is seed_vector(mp, 8))
    S = seed_vector(mp, 8) if not M3SQ3 else seed_vector_general(mp, 8, MASSES[:4])
    y0 = [S[MASTERS[i]].coeff(K) for (i, K) in STATE]

    # Frobenius series of s*A around 0.  Radius of convergence = rho (nearest
    # nonzero singularity of the connection, 3-2*sqrt(2)), so the term ratio of
    # the sum at s1 is CERTIFIED r_f = |s1|/rho < 1 (fail-closed check below).
    rho = CFG["rho"](mp)          # 3 - 2*sqrt(2) at the default; 4 - 2*sqrt(3) at 1,4,9,3,0
    if not abs(s1) < rho:
        raise RuntimeError(
            f"kite-threshold: |s1| = {mp.nstr(abs(s1), 8)} >= rho = {mp.nstr(rho, 8)}"
            " — Frobenius series diverges at the start point (fail-closed)")
    r_f = abs(s1)/rho
    rfac_f = r_f/(1 - r_f)
    Nser0 = int(mp.ceil((mp.mp.dps + 12)*mp.log(10)/(-mp.log(abs(s1)/rho)))) + 10
    NSER_CAP = NCAP_FAC*Nser0
    Nser = Nser0

    def build_Bser(nser):
        # prefix-stable: series_div(k) depends only on P, Q and out[<k], so a
        # longer call reproduces the shorter one exactly => regrowing Bser at a
        # larger nser is EXACT continuation of the same recurrences
        out = []
        for (i, j, P0, P1, Q) in ENT:
            if Q[0] == 0:
                ser0 = series_div(P0, Q[1:], nser+1, mp) if P0 else None
                ser1 = series_div(P1, Q[1:], nser+1, mp) if P1 else None
            else:
                ser0 = ([mp.mpf(0)] + series_div(P0, Q, nser, mp)) if P0 else None
                ser1 = ([mp.mpf(0)] + series_div(P1, Q, nser, mp)) if P1 else None
            out.append((i, j, ser0, ser1))
        return out

    Bser = build_Bser(Nser)

    D = mp.zeros(N, N); E = mp.zeros(N, N)
    for (i, j, s0, s1_) in Bser:
        if s0: D[i, j] += s0[0]
        if s1_: E[i, j] += s1_[0]

    # analytic-branch consistency: A_{-1} . V = 0
    scale = max(abs(x) for x in y0)
    worst = mp.mpf(0)
    for K in ORDERS:
        for i in range(N):
            acc = mp.mpf(0)
            for j in range(N):
                acc += D[i, j]*y0[IDX[(j, K)]]
                if K-1 >= KMIN: acc += E[i, j]*y0[IDX[(j, K-1)]]
            worst = max(worst, abs(acc))
    kerd = -mp.log10(worst/scale) if worst > 0 else mp.mpf(dps)
    assert kerd > dps - 15, f"seed NOT in ker A_-1 (only {kerd} digits) -- construction violated"
    if not quiet:
        print(f"[seed] ker A_-1 residual: {mp.nstr(worst/scale, 3)} (analytic branch OK)")

    # 2026-07-06: independent seed compare (RAISES on mutation;
    # the kernel residual alone cannot catch t2-direction perturbations).
    # 2026-07-06b: skipped under --point-only (control battery;
    # the value path -- seed, ker assert, certified bounds -- is untouched).
    if point_only:
        if not quiet:
            print("[point-only] control battery SKIPPED: seed integrity gate "
                  "+ oracle gate-table compare (per-step certified bounds "
                  "and the ker A_-1 analytic-branch assert retained)")
    elif not M3SQ3:
        seed_integrity_gate(mp, dps, quiet=quiet)
    else:
        seed_integrity_gate_general(mp, dps, MASSES[:4], quiet=quiet)

    # Frobenius recursion, blockwise in K (graded A_{-1} is block lower-triangular)
    a = [list(y0)]

    def frob_extend(lo, hi):
        # a[n_] for n_ in [lo, hi): EXACT continuation — reads the current Bser
        for n_ in range(lo, hi):
            rhs = [mp.mpf(0)]*NS
            for (i, j, ser0, ser1) in Bser:
                for K in ORDERS:
                    acc = mp.mpf(0)
                    if ser0:
                        jj = IDX[(j, K)]
                        kmax = min(n_, len(ser0)-1)
                        for k in range(1, kmax+1):
                            c = ser0[k]
                            if c: acc += c*a[n_-k][jj]
                    if ser1 and K-1 >= KMIN:
                        jj = IDX[(j, K-1)]
                        kmax = min(n_, len(ser1)-1)
                        for k in range(1, kmax+1):
                            c = ser1[k]
                            if c: acc += c*a[n_-k][jj]
                    rhs[IDX[(i, K)]] += acc
            an = [mp.mpf(0)]*NS
            M_ = mp.eye(N)*n_ - D
            for K in ORDERS:
                b = mp.zeros(N, 1)
                for i in range(N):
                    b[i] = rhs[IDX[(i, K)]]
                    if K-1 >= KMIN:
                        for j in range(N):
                            if E[i, j]: b[i] += E[i, j]*an[IDX[(j, K-1)]]
                x = mp.lu_solve(M_, b)
                for i in range(N):
                    an[IDX[(i, K)]] = x[i]
            a.append(an)

    frob_extend(1, Nser)

    # certified last-term (trailing-window) gate on the s=0 Frobenius sum:
    # tail <= max(|a_n s1^n| over last TAIL_WINDOW terms, all components) * r_f/(1-r_f)
    frob_escal = 0
    while True:
        bnd = mp.mpf(0)
        pw_t = abs(s1)**(len(a) - TAIL_WINDOW)
        for n_ in range(len(a) - TAIL_WINDOW, len(a)):
            m_ = max(abs(v) for v in a[n_])
            t_ = m_*pw_t
            if t_ > bnd: bnd = t_
            pw_t *= abs(s1)
        err_frob = bnd*rfac_f
        if err_frob < tol:
            break
        if Nser >= NSER_CAP:
            raise RuntimeError(
                "kite-threshold Frobenius sum: certified tail bound NOT met at "
                f"s1 = {mp.nstr(s1, 10)}: achieved bound {mp.nstr(err_frob, 5)} >= "
                f"tol {mp.nstr(tol, 5)} at Nser = {Nser} "
                f"(cap {NSER_CAP} = {NCAP_FAC} x initial {Nser0})")
        Nser2 = min(2*Nser, NSER_CAP)
        Bser = build_Bser(Nser2)          # exact continuation (prefix-stable)
        frob_extend(len(a), Nser2)
        Nser = Nser2
        frob_escal += 1

    y = [mp.mpf(0)]*NS
    pw = mp.mpf(1)
    for n_ in range(len(a)):
        for c in range(NS): y[c] += a[n_][c]*pw
        pw *= s1
    if not quiet:
        print(f"[bound] Frobenius sum at s1 = {mp.nstr(s1, 8)}: certified tail <= "
              f"{mp.nstr(err_frob, 3)}  (tol {mp.nstr(tol, 3)}, Nser = {Nser}, "
              f"escalations {frob_escal})")

    POLES = CFG["poles"](mp)      # the selected line's singular points at this dps (the default: the literal list)
    radius = lambda x0: min(abs(x0-p) for p in POLES)

    def Ms_at(x0, ordN):
        # prefix-stable in ordN (series_div): a longer call reproduces the
        # shorter one exactly => order growth in stepf is EXACT continuation
        MsD = [dict() for _ in range(ordN+1)]
        for (i, j, P0, P1, Q) in ENT:
            Qs = taylor_shift(Q, x0, mp)
            for (P, band) in ((P0, 0), (P1, 1)):
                if not P: continue
                ser = series_div(taylor_shift(P, x0, mp), Qs, ordN+1, mp)
                for K in ORDERS:
                    if K-band < KMIN: continue
                    nn = IDX[(i, K)]; mm = IDX[(j, K-band)]
                    for k in range(ordN+1):
                        if ser[k]: MsD[k].setdefault(nn, []).append((mm, ser[k]))
        return MsD

    NCAP = NCAP_FAC*order

    def stepf(yv, x0, h, step):
        # per-step certified trailing-window tail gate: the step
        # rule |h| <= 0.3*radius(x0) certifies the Taylor term ratio r =
        # |h|/dmin <= 0.3, so tail <= max(|c_n h^n| over the last TAIL_WINDOW
        # terms, all components) * r/(1-r).  `order` is the STARTING guess;
        # on failure the SAME recurrences continue exactly at doubled order.
        dmin = radius(x0)
        rr = abs(h)/dmin
        rfac = rr/(1 - rr)
        Nord = order
        MsD = Ms_at(x0, Nord)
        acur = [list(yv)]
        for n_ in range(Nord+1):
            ss = [mp.mpf(0)]*NS
            for k in range(n_+1):
                am = acur[n_-k]
                for nn, lst in MsD[k].items():
                    acc = ss[nn]
                    for (mm, c) in lst: acc += c*am[mm]
                    ss[nn] = acc
            acur.append([v/(n_+1) for v in ss])
        escal = 0
        while True:
            bound = mp.mpf(0)
            hn = abs(h)**(len(acur) - TAIL_WINDOW)
            for n_ in range(len(acur) - TAIL_WINDOW, len(acur)):
                m_ = max(abs(v) for v in acur[n_])
                t_ = m_*hn
                if t_ > bound: bound = t_
                hn *= abs(h)
            bound *= rfac
            if bound < tol:
                break
            if Nord >= NCAP:
                raise RuntimeError(
                    "kite-threshold stepf: certified tail bound NOT met at "
                    f"step {step}, x0 = {mp.nstr(x0, 20)}, h = {mp.nstr(h, 10)}: "
                    f"achieved bound {mp.nstr(bound, 5)} >= tol {mp.nstr(tol, 5)} "
                    f"at Taylor order N = {Nord} "
                    f"(cap {NCAP} = {NCAP_FAC} x initial {order})")
            N2 = min(2*Nord, NCAP)
            MsD = Ms_at(x0, N2)           # exact continuation (prefix-stable)
            for n_ in range(len(acur)-1, N2+1):
                ss = [mp.mpf(0)]*NS
                for k in range(n_+1):
                    am = acur[n_-k]
                    for nn, lst in MsD[k].items():
                        acc = ss[nn]
                        for (mm, c) in lst: acc += c*am[mm]
                        ss[nn] = acc
                acur.append([v/(n_+1) for v in ss])
            Nord = N2
            escal += 1
        out = [mp.mpf(0)]*NS
        hp = mp.mpf(1)
        for n_ in range(len(acur)):
            for c in range(NS): out[c] += acur[n_][c]*hp
            hp *= h
        return out, bound, escal

    res = {}
    x = s1
    step = 0
    err_steps = mp.mpf(0)
    worst_step = mp.mpf(0)
    step_escal = 0
    for tgt in tvals:
        while abs(x - tgt) > mp.mpf(10)**(-mp.mp.dps+5):
            h = -min(mp.mpf('0.3')*radius(x), abs(tgt - x))
            step += 1
            y, bnd, esc = stepf(y, x, h, step)
            err_steps += bnd
            if bnd > worst_step: worst_step = bnd
            step_escal += esc
            x += h
        # value-level certified bound: TOP_eps0 is a single state component with
        # exact prefactor 1, so l1 propagation = plain sum of the state bounds
        rec = {"TOP_eps0": y[IDX[(TOP, 0)]],
               "err_bound": err_frob + err_steps,
               "err_frob": err_frob, "err_steps": err_steps,
               "worst_step": worst_step, "nsteps": step,
               "escal": frob_escal + step_escal}
        if want_state:
            rec["y"] = list(y)
            rec["IDX"] = IDX; rec["MASTERS"] = MASTERS; rec["ENT"] = ENT
            rec["ORDERS"] = ORDERS; rec["KMIN"] = KMIN
        # 2026-07-06: independent AMFlow-oracle RAISING gate.
        # Vendored strings are gate-cache only (provenance at ORACLE above);
        # the recompute path IS the definition.  Threshold min(dps-8,
        # oracle_digits-2): -8 = the --check roundoff margin, -2 = oracle
        # certification margin.  Fail => RuntimeError (rc != 0, fail-closed).
        # 2026-07-06b: skipped under --point-only (gate table).
        if point_only:
            res[str(tgt)] = rec
            continue
        ora_hit = None
        for ok_, (ostr, odig) in ORACLE.items():
            if tgt == mp.mpf(ok_):
                ora_hit = (ok_, ostr, odig); break
        if ora_hit is not None:
            ok_, ostr, odig = ora_hit
            with mp.workdps(max(dps, 130) + 10):
                oval = mp.mpf(ostr)
                dvv = abs((rec["TOP_eps0"] - oval)/oval)
            odd = -mp.log10(dvv) if dvv > 0 else mp.mpf(dps + odig)
            thr = min(dps - 8, odig - 2)
            if not odd >= thr:
                raise RuntimeError(
                    f"kite-threshold ORACLE GATE FAILED at t={ok_}: agreement "
                    f"{mp.nstr(odd, 5)} digits < threshold {thr} "
                    f"(dps {dps}, oracle certified {odig}d) -- fail-closed")
            if not quiet:
                print(f"  [oracle] t={ok_}: agrees {mp.nstr(odd, 5)} d vs "
                      f"held-out AMFlow oracle (thr {thr}, oracle {odig}d) OK")
        elif not quiet:
            print(f"  [oracle] t={mp.nstr(tgt, 10)}: not in oracle table "
                  "(no gate at this point)")
        res[str(tgt)] = rec
    return res


def derive_AB(dps):
    """Frame constants A,B at t0=-2: DERIVED from the vacuum-seeded transport."""
    import mpmath as mp
    out = run(dps, ["-2"], want_state=True, quiet=True)
    mp.mp.dps = dps
    rec = out[list(out.keys())[0]]
    y, IDX, MASTERS, ENT = rec["y"], rec["IDX"], rec["MASTERS"], rec["ENT"]
    N = len(MASTERS); TOP = MASTERS.index((1, 1, 1, 1, 1))
    t0 = mp.mpf(-2)
    top0 = y[IDX[(TOP, 0)]]
    # DE-exact derivative of TOP at eps^0: sum_j [A0 M_j|eps0 + A1 M_j|eps-1]
    evalP = lambda P, x: sum(c*x**k for k, c in enumerate(P)) if P else mp.mpf(0)
    dtop = mp.mpf(0)
    for (i, j, P0, P1, Q) in ENT:
        if i != TOP: continue
        q = evalP(Q, t0)
        dtop += evalP(P0, t0)/q*y[IDX[(j, 0)]] + evalP(P1, t0)/q*y[IDX[(j, -1)]]
    F = 4*t0*top0
    dF = 4*top0 + 4*t0*dtop
    # BMSW periods of a (1,2,3)-mass sunrise curve -- NOT a sub-topology of this family; the --AB
    # frame is kept for the record (see the docstring) -- Euclidean-real frame,
    # WITH exact analytic t-derivatives (2026-07-06).
    #
    # DERIVATION (replaces the central finite difference h = 10^-(dps//3),
    # whose ~h^2 truncation froze the dps30-vs-60 A,B agreement at ~21
    # digits; every line below is exact closed-form differentiation of the
    # construction itself -- no step size, no truncation):
    #   rad(t) = 3 prod_i sqrt(mu_i^2 - t)
    #     => rad'(t) = -(rad/2) * sum_i 1/(mu_i^2 - t)
    #   e1 = (-t^2 + 2 M t + Delta + rad)/24 => e1' = (-2t + 2M + rad')/24
    #   e2 = (-t^2 + 2 M t + Delta - rad)/24 => e2' = (-2t + 2M - rad')/24
    #   e3 = (2t^2 - 4 M t - 2 Delta)/24     => e3' = (4t - 4M)/24
    #   Z1 = e3 - e2, Z3 = e1 - e2, k2 = Z1/Z3
    #     => k2' = (Z1' Z3 - Z1 Z3')/Z3^2
    #   dK/dm = (E(m) - (1-m) K(m)) / (2 m (1-m))    [mpmath parameter m=k^2]
    #   p1 = 2 Z3^{-1/2} K(k2)
    #     => p1' = -Z3' Z3^{-3/2} K(k2) + 2 Z3^{-1/2} (dK/dm)(k2) k2'
    #   p2 = 2 Z3^{-1/2} K(1-k2)
    #     => p2' = -Z3' Z3^{-3/2} K(1-k2) - 2 Z3^{-1/2} (dK/dm)(1-k2) k2'
    # On the Euclidean axis t<0: mu_i^2 - t > 0, Z3 > 0, 0 < k2 < 1 (checked
    # numerically along the whole axis, e.g. k2(-2) = 0.83113...), so all
    # quantities are real, mp.re() is the identity, and d/dt commutes with it.
    #
    # RAISING cross-check: eliminating the Z3' terms and using the Legendre
    # relation E(m)K(1-m) + E(1-m)K(m) - K(m)K(1-m) = pi/2 gives the exact
    # closed-form Wronskian
    #   W = p1 p2' - p2 p1' = -pi k2' / (Z3 k2 (1-k2)),
    # enforced below to dps-5 digits (fail-closed RuntimeError).
    def periods_and_derivs(t):
        m1, m2, m3 = mp.mpf(1), mp.mpf(2), mp.mpf(3)
        M100 = m1**2 + m2**2 + m3**2
        mu1, mu2, mu3, mu4 = -m1+m2+m3, m1-m2+m3, m1+m2-m3, m1+m2+m3
        Delta = mu1*mu2*mu3*mu4
        rad = 3*mp.sqrt(mu1**2-t)*mp.sqrt(mu2**2-t)*mp.sqrt(mu3**2-t)*mp.sqrt(mu4**2-t)
        radp = -(rad/2)*(1/(mu1**2-t) + 1/(mu2**2-t) + 1/(mu3**2-t) + 1/(mu4**2-t))
        e1 = (-t*t + 2*M100*t + Delta + rad)/24
        e2 = (-t*t + 2*M100*t + Delta - rad)/24
        e3 = (2*t*t - 4*M100*t - 2*Delta)/24
        e1p = (-2*t + 2*M100 + radp)/24
        e2p = (-2*t + 2*M100 - radp)/24
        e3p = (4*t - 4*M100)/24
        Z1, Z3 = e3-e2, e1-e2
        Z1p, Z3p = e3p-e2p, e1p-e2p
        k2 = Z1/Z3
        k2p = (Z1p*Z3 - Z1*Z3p)/Z3**2
        K1, K2c = mp.ellipk(k2), mp.ellipk(1-k2)
        E1, E2c = mp.ellipe(k2), mp.ellipe(1-k2)
        dKdm = lambda K, E, m: (E - (1-m)*K)/(2*m*(1-m))
        sZ = mp.sqrt(Z3)
        p1 = 2/sZ*K1
        p2 = 2/sZ*K2c
        d1 = -Z3p/(Z3*sZ)*K1 + 2/sZ*dKdm(K1, E1, k2)*k2p
        d2 = -Z3p/(Z3*sZ)*K2c - 2/sZ*dKdm(K2c, E2c, 1-k2)*k2p
        # Legendre-relation Wronskian gate (fail-closed)
        W_num = p1*d2 - p2*d1
        W_cf = -mp.pi*k2p/(Z3*k2*(1-k2))
        wd = abs((W_num - W_cf)/W_cf)
        if not wd < mp.mpf(10)**(-(dps - 5)):
            raise RuntimeError(
                "kite-threshold --AB: Legendre-Wronskian cross-check FAILED: "
                f"|W_num/W_closed - 1| = {mp.nstr(wd, 5)} >= 1e-{dps-5} "
                "(analytic period derivatives inconsistent -- fail-closed)")
        return p1, p2, d1, d2
    old = mp.mp.dps; mp.mp.dps = dps + 15   # guard digits: roundoff only, no truncation
    p1, p2, d1, d2 = periods_and_derivs(t0)
    mp.mp.dps = old
    W = p1*d2 - p2*d1
    A = (F*d2 - dF*p2)/W
    B = (dF*p1 - F*d1)/W
    return A, B, F, dF, (p1, p2, d1, d2)


# ---------------------------------------------------------------------------
# --AB-ring (2026-09-06): the certified A, B strings and the PSLQ rings as
# PINS.  The value path (derive_AB and the seed block above) is untouched:
# this tier reads the sha256-pinned kite-threshold-AB-ring.json, prints what
# it carries (the four strings of the --AB tier at working precision 420,
# certified to n_cert digits = the agreement of the two highest working
# precisions minus 2 guard digits; the record's literals beside; the three
# rings REC / T0 / T1 with their member names and the heights to which the
# PSLQ scan of A, B, A/B, A varpi - B pi, F, F', A sqrt(3 sqrt57),
# B sqrt(3 sqrt57) returned NULL under the house law and the strict law;
# the receipts' sha256 prefixes), then RE-DERIVES A, B, F, F' by derive_AB
# at --dps and compares each with its pinned string.  The printed agreement
# is capped at min(n_cert, dps - 8); the bar is the served two-precision
# form dps - 8, itself capped at n_cert (above --dps 334 the pinned strings
# carry no further certified digit).  FAIL by name (exit 1) below the bar.
# --mutate-ring alters one digit of one pinned string in memory (decimal
# digit 10 of A, or half the cap when the cap is smaller -- below the bar at
# every served --dps (>= 30; --AB-ring refuses lower); the file and its pin
# untouched): the compare must FAIL by name.
# Exit: 0 PASS; 1 FAIL; 3 the pins file altered (sha256 mismatch, named);
# 4 the pins file missing (named).
# ---------------------------------------------------------------------------
AB_RING_FILE = "kite-threshold-AB-ring.json"
AB_RING_SLACK = 8            # the served two-precision bar form: dps - 8
AB_RING_DPS_FLOOR = 30       # --AB-ring floor: below it the cap min(n_cert, dps - 8) makes the compare and the --mutate-ring control pass by construction
AB_RING_MUTATE_DIGIT = 10    # --mutate-ring: the decimal digit of A altered in memory (or half the cap, if smaller)


def _pinned_ring_file():
    """The --AB-ring pins file, sha256-pinned in PINS: a byte change is REFUSED
    (exit 3, the recorded and recomputed sha256 named); the file missing is
    refused by name with exit 4."""
    import hashlib
    path = os.path.join(HERE, AB_RING_FILE)
    want = PINS[AB_RING_FILE]
    if not os.path.exists(path):
        sys.stderr.write(f"kite-threshold-evaluate REFUSED: --AB-ring pins file {AB_RING_FILE} is MISSING "
                         f"(recorded sha256 {want}); not serving --AB-ring\n")
        raise SystemExit(4)
    have = hashlib.sha256(open(path, "rb").read()).hexdigest()
    if have != want:
        pos = next((k + 1 for k, (x, y) in enumerate(zip(want, have)) if x != y), 0)
        sys.stderr.write(f"kite-threshold-evaluate REFUSED: {AB_RING_FILE} integrity pin mismatch (recorded {want}, "
                         f"recomputed {have}; first differing hex position {pos} of 64, 1-based) -- "
                         f"the shipped file was altered; not serving --AB-ring\n")
        raise SystemExit(3)
    return path


def _first_digits(s, n):
    """The prefix of the decimal string s holding its first n digits."""
    k = 0
    for i, c in enumerate(s):
        if c.isdigit():
            k += 1
            if k == n:
                return s[:i + 1]
    return s


def _mutate_digit(s, k):
    """s with its k-th digit after the decimal point changed (a control)."""
    i = s.index(".") + k
    assert s[i].isdigit()
    return s[:i] + str((int(s[i]) + 1) % 10) + s[i + 1:]


def ab_ring_tier(dps, mutate=False):
    """--AB-ring: print the pinned certified strings and rings, then re-derive
    A, B, F, F' live and compare (RAISING).  Returns the exit code."""
    import mpmath as mp
    ring = json.load(open(_pinned_ring_file()))
    cert, pins, T = ring["certification"], ring["pins"], ring["targets"]
    n_cert = int(cert["n_cert"])
    keys = ("A", "B", "F_m2", "dF_m2")
    labels = {"A": "A", "B": "B", "F_m2": "F(-2)", "dF_m2": "F'(-2)"}
    print(f"[pins] {AB_RING_FILE} sha256 {PINS[AB_RING_FILE][:16]}... verified; receipt of record "
          f"{pins['receipt_of_record_sha256_16']}, target strings {pins['target_strings_sha256_16']}, record literals "
          f"{pins['record_literals_sha256_16']}, ring member values {pins['ring_member_values_two_precision_sha256_16']}, "
          f"pslq harness {pins['pslq_harness_sha256_16']}, {len(pins['ring_scan_receipts_sha256_16'])} ring-scan receipts")
    print(f"[targets] the --AB tier (as served at {cert['derived_by_script_sha256_16']}; derive_AB byte-identical in this release) at working precisions "
          f"{cert['legs_dps']}: the {cert['string_dps']}-digit strings certified to n_cert = {n_cert} digits = the "
          f"agreement of the two highest legs (min {min(cert['agreement_330_vs_420_digits'].values()):.1f} d) minus 2 guard digits")
    strings = {k: T[k]["string"] for k in keys}
    for k in keys:
        s = strings[k]
        print(f"  {labels[k]:6s} = {_first_digits(s, 60)}...  [{sum(c.isdigit() for c in s)} digits; n_cert {T[k]['n_cert']}]")
        if "record_literal" in T[k]:
            print(f"         record literal {_first_digits(T[k]['record_literal'], 60)}...  [{T[k]['record_literal_digits']} "
                  f"digits; agrees with the string to {T[k]['record_literal_vs_string_digits']:.1f} d = the record's own floor]")
    print("[rings] PSLQ scan of A, B, A/B, A varpi - B pi, F, F', A sqrt(3 sqrt57), B sqrt(3 sqrt57): the heights H to which "
          "the null holds under the house law (n+1) log10 H + 20 <= the lower leg / the strict law 2 n log10 H + 20 <= the digits used")
    for t in ("REC", "T0", "T1"):
        r = ring["rings"][t]
        hh, hs = int(r["max_height_scanned_house_law"]), int(r["max_height_certified_strict_2n_law"])
        eh, es = len(str(hh)) - 1, len(str(hs)) - 1
        assert hh == 10**eh and hs == 10**es, (t, hh, hs)
        print(f"  {t}: {r['n_members']} members, {r['verdict']} to 10^{eh} (house law) / 10^{es} (strict law)")
        print(f"      members: {', '.join(r['members'])}")
        print(f"      positive control: {r['positive_control']}")
    ss = ring["scan_summary"]
    print(f"  {ss['null_target_scans']} null target scans, {ss['hits']} hits, {ss['refused_rungs']} refused rungs")
    cap = min(n_cert, dps - AB_RING_SLACK)
    bar = cap
    if mutate:
        # the altered digit sits below the bar at every served --dps (>= 30; --AB-ring refuses lower):
        # digit 10, or half the cap when the cap is smaller, so the control is caught at every served precision
        k = min(AB_RING_MUTATE_DIGIT, max(1, cap // 2))
        strings["A"] = _mutate_digit(strings["A"], k)
        print(f"[mutate-ring] CONTROL: the pinned A string altered in memory at decimal digit {k} "
              "(the file and its pin untouched): the compare below must FAIL by name")
    t0 = time.time()
    A, B, F, dF, per = derive_AB(dps)
    dt = time.time() - t0
    live = {"A": A, "B": B, "F_m2": F, "dF_m2": dF}
    print(f"[re-derive] derive_AB at dps {dps} ({dt:.1f}s): agreement vs the pinned strings, printed capped at "
          f"min(n_cert {n_cert}, dps - {AB_RING_SLACK}) = {cap}; bar {bar} (the two-precision form dps - {AB_RING_SLACK}, capped at n_cert)")
    ok = True
    for k in keys:
        with mp.workdps(dps + 40):
            pin = mp.mpf(strings[k])
            x = mp.mpf(live[k])
            raw = -mp.log10(abs((x - pin) / pin)) if x != pin else mp.mpf(dps + 40)
            shown = min(raw, mp.mpf(cap))
            stat = "PASS" if shown >= bar else "FAIL"
            if stat == "FAIL":
                ok = False
            print(f"  {labels[k]:6s}: {mp.nstr(shown, 5)} d (measured {mp.nstr(raw, 5)}, cap {cap}) vs bar {bar}  [{stat}]")
    print("AB-RING PASSED: the live derivation reproduces every pinned certified string to the bar" if ok else
          "AB-RING FAILED: a live value is below the bar against its pinned string (fail-closed)")
    return 0 if ok else 1


def main():
    # line-buffered stdout: progress lines land immediately even when piped
    sys.stdout.reconfigure(line_buffering=True)
    ap = argparse.ArgumentParser(
        description="Threshold-configuration kite top master TOP(t): live\n"
                    "transport from the classical p^2=0 seed.  Bare invocation =\n"
                    "quick demo (dps 30: one held-out gate point + the frame\n"
                    "constants A,B); --full = the six-point gate table at dps 100.\n"
                    "--masses 1,4,9,3,0 = the line with the fourth squared mass 3\n"
                    "(its own connection, seed table and independent reference at\n"
                    "t = -2, -7/3; poles at s = 0, 1, 9, 25, 297/8, 4 -+ 2 sqrt3,\n"
                    "12 -+ 6 sqrt3).",
        epilog="TIERS (measured walls: this host, a shared 96-core server at loadavg ~130, "
               "wall clock): bare = the 1,4,9,2,0 quick demo at dps 30 (66 s); "
               "--dps 100 --point=-1 --point=-7/2 (216 s; with --check 673 s); "
               "--dps 100 --AB (198 s); --full (267 s); --dps 100 --AB-ring = the certified A, B strings "
               "and the PSLQ rings (pins), then a live re-derivation (the --AB wall + the file read).  "
               "--masses 1,4,9,3,0 = the quick demo of the 1,4,9,3,0 line, --point=-2 at dps 30 with the "
               "22-entry reference table (32 s); --masses 1,4,9,3,0 --dps 100 --point=-2 "
               "--point=-7/3 --check (633 s); the same at --dps 145 = the two reference "
               "points at the precision of record, bar 98 (1083 s).  Exit codes: 0 pass; "
               "1 a raising gate (seed gate, seed-table gate, oracle gate, reference bar, two-precision, "
               "certified tail); 2 usage/domain refusal (unsupported --masses, --full/--AB at the new "
               "line, --point-only misuse, t above the transport start); 3 an integrity pin mismatch "
               "on a shipped data file (the 1,4,9,3,0 line; the --AB-ring pins file); 4 the --AB-ring "
               "pins file missing.")
    ap.add_argument("--masses", metavar="M1,M2,M3,M4,M5", default="1,4,9,2,0",
                    choices=["1,4,9,2,0", "1,4,9,3,0"],
                    help="the five squared masses of (D1,D2,D3,D4,D5): 1,4,9,2,0 (default) or "
                         "1,4,9,3,0; any other five-tuple is refused by name")
    ap.add_argument("--dps", type=int, default=None,
                    help="working precision, decimal digits (default: 30 for the "
                         "quick demo, 100 otherwise)")
    ap.add_argument("--full", action="store_true",
                    help="full held-out gate table: all six oracle points "
                         "(the pre-2026-09 default)")
    ap.add_argument("--point", action="append", default=None,
                    help="t value (rational string); repeatable; default = 6 gate points")
    ap.add_argument("--check", action="store_true", help="re-run at dps+60 and diff")
    ap.add_argument("--AB", action="store_true", help="derive frame constants A,B at t0=-2")
    ap.add_argument("--AB-ring", dest="AB_ring", action="store_true",
                    help="print the certified A,B (and F, F') strings and the PSLQ rings from the "
                         "sha256-pinned kite-threshold-AB-ring.json, then re-derive A,B live at --dps "
                         "(default 100) and compare against the pinned strings (RAISING)")
    ap.add_argument("--mutate-ring", action="store_true",
                    help="control: alter one digit of one pinned string in memory (the file's pin "
                         "untouched) -- the --AB-ring compare must FAIL by name; requires --AB-ring")
    ap.add_argument("--order", type=int, default=None)
    ap.add_argument("--s1", default=None)
    ap.add_argument("--point-only", action="store_true",
                    help="plain point evaluation: skip the control batteries "
                         "(seed integrity gate + oracle gate table); the value "
                         "path incl. all certified bounds is unchanged; "
                         "requires --point; incompatible with --check")
    args = ap.parse_args()

    if M3SQ3:
        # 2026-09-05: the 1,4,9,3,0 line is a --point evaluator; the six-point
        # oracle table and the frame decomposition are the 1,4,9,2,0 record.
        if args.full:
            ap.error("--full is the six-point oracle table of the 1,4,9,2,0 line; at "
                     "--masses 1,4,9,3,0 use --point=-2 and/or --point=-7/3 (or no "
                     "--point for the quick demo)")
        if args.AB:
            ap.error("--AB is the frame decomposition of the 1,4,9,2,0 line at t0=-2; "
                     "not served at --masses 1,4,9,3,0")
        if args.AB_ring:
            ap.error("--AB-ring is the frame decomposition of the 1,4,9,2,0 line at t0=-2 (its pinned "
                     "strings and rings); not served at --masses 1,4,9,3,0")

    quick = not (args.AB or args.AB_ring or args.check or args.full or args.point)
    if args.dps is None:
        args.dps = 30 if quick else 100
    if M3SQ3 and quick:
        # the quick demo of the new line: the reference point t=-2 at dps 30
        args.point = [CFG["quick_point"]]
        quick = False
        print(f"kite-threshold quick demo of the line --masses {','.join(map(str, MASSES))} "
              f"(dps {args.dps}): the reference point t={CFG['quick_point']} with the 22-entry "
              f"independent-reference table; --point/--dps/--check override")

    if args.mutate_ring and not args.AB_ring:
        ap.error("--mutate-ring is the control of --AB-ring and requires it")
    if args.AB and args.AB_ring:
        ap.error("--AB and --AB-ring are separate tiers: the live derivation, or the pinned "
                 "strings and rings followed by the live derivation and the compare")
    if args.AB_ring and args.dps < AB_RING_DPS_FLOOR:
        # 2026-09-06b: below the floor the cap min(n_cert, dps - 8) is too small for the compare to mean anything
        ap.error(f"--AB-ring needs --dps >= {AB_RING_DPS_FLOOR}: below it the cap min(n_cert, dps - {AB_RING_SLACK}) "
                 "makes the compare and the --mutate-ring control pass by construction")
    if args.point_only:
        # batteries remain default-on for --check and bare runs (fail-closed)
        if args.check:
            ap.error("--point-only and --check are mutually exclusive: "
                     "--check keeps the full battery by design")
        if not args.point:
            ap.error("--point-only requires at least one --point")

    import mpmath as mp
    mp.mp.dps = args.dps        # set INSIDE main, after argparse

    if quick:
        # quick demo (2026-09-03 default): one held-out gate point with the
        # full control battery (seed integrity gate + oracle gate), then the
        # frame constants A,B -- everything at dps 30, first line immediate.
        print(f"kite-threshold quick demo (dps {args.dps}): held-out gate point "
              f"t=-2, then frame constants A,B; --full = six-point table")
        t0 = time.time()
        try:
            res = run(args.dps, ["-2"])
        except DomainRefusal as e:
            print(f"REFUSED: {e}")
            sys.exit(2)
        rec = res[list(res.keys())[0]]
        print(f"t=-2: TOP_eps0 = {mp.nstr(rec['TOP_eps0'], args.dps)}")
        print(f"  [bound] certified |Delta TOP_eps0| <= {mp.nstr(rec['err_bound'], 3)}  "
              f"(Frobenius {mp.nstr(rec['err_frob'], 3)} + {rec['nsteps']} transport steps, "
              f"worst step {mp.nstr(rec['worst_step'], 3)}; escalations {rec['escal']})")
        A, B, F, dF, per = derive_AB(args.dps)
        print(f"F(-2)  = {mp.nstr(F, args.dps)}")
        print(f"F'(-2) = {mp.nstr(dF, args.dps)}")
        print(f"A = {mp.nstr(A, args.dps)}")
        print(f"B = {mp.nstr(B, args.dps)}")
        print(f"[measured: {time.time()-t0:.1f}s at dps={args.dps}]")
        return

    if args.AB_ring:
        # 2026-09-06: the pinned certified strings and rings first, then the live
        # re-derivation and the compare (RAISING); the value path is derive_AB as is
        sys.exit(ab_ring_tier(args.dps, mutate=args.mutate_ring))

    if args.AB:
        t0 = time.time()
        A, B, F, dF, per = derive_AB(args.dps)
        print(f"F(-2)  = {mp.nstr(F, args.dps)}")
        print(f"F'(-2) = {mp.nstr(dF, args.dps)}")
        print(f"A = {mp.nstr(A, args.dps)}")
        print(f"B = {mp.nstr(B, args.dps)}")
        print(f"[{time.time()-t0:.1f}s]")
        return

    pts = args.point or GATE_POINTS
    t0 = time.time()
    if M3SQ3:
        from fractions import Fraction
        key_of = {str(mp.mpf(Fraction(p).numerator)/mp.mpf(Fraction(p).denominator)): p for p in pts}
        if args.point_only:
            print("[point-only] the seed-table gate and the independent-reference table of this "
                  "line are SKIPPED too (value path unchanged)")
        else:
            m3sq3_seed_gate(mp, args.dps)
    try:
        res = run(args.dps, pts, order=args.order, s1=args.s1,
                  point_only=args.point_only, want_state=M3SQ3)
    except DomainRefusal as e:
        # named refusal, rc=2 (2026-07-06b; was an infinite loop)
        print(f"REFUSED: {e}")
        sys.exit(2)
    dt = time.time() - t0
    for k, v in res.items():
        print(f"t={k}: TOP_eps0 = {mp.nstr(v['TOP_eps0'], args.dps)}")
        print(f"  [bound] certified |Delta TOP_eps0| <= {mp.nstr(v['err_bound'], 3)}  "
              f"(Frobenius {mp.nstr(v['err_frob'], 3)} + {v['nsteps']} transport steps, "
              f"worst step {mp.nstr(v['worst_step'], 3)}; escalations {v['escal']})")
        if M3SQ3 and not args.point_only:
            m3sq3_reference_table(mp, v, key_of[k], args.dps)
    print(f"[measured: {dt:.1f}s at dps={args.dps}]")

    if args.check:
        t1 = time.time()
        res2 = run(args.dps + 60, pts, order=args.order, s1=args.s1, quiet=True,
                   want_state=M3SQ3)
        dt2 = time.time() - t1
        print(f"[check re-run: {dt2:.1f}s at dps={args.dps+60}]")
        ok = True
        # 2026-09-05: the rerun keys its points by str(t) at dps+60, which differs
        # from the dps key at a non-dyadic t (e.g. -7/3: KeyError before this
        # line); both runs hold the same targets in the same (sorted) order, so
        # the pairs are taken positionally.  Dyadic points: keys and output
        # unchanged.
        for k, k2 in zip(res, res2):
            a_, b_ = res[k]["TOP_eps0"], res2[k2]["TOP_eps0"]
            dd = -mp.log10(abs((a_-b_)/b_)) if a_ != b_ else mp.mpf(args.dps+60)
            stat = "OK" if dd >= args.dps - 8 else "FAIL"
            if stat == "FAIL": ok = False
            print(f"  t={k}: two-precision agreement {mp.nstr(dd, 5)} digits  [{stat}]")
            if M3SQ3:
                tp = _m3sq3_two_precision(mp, res[k], res2[k2], key_of[k], args.dps)
                if tp is False: ok = False
        print("CHECK PASSED" if ok else "CHECK FAILED")
        if not ok:
            sys.exit(1)     # agreement gate is RAISING (fail-closed)


if __name__ == "__main__":
    main()
