#!/usr/bin/env python3
"""
tbs_registry_gen.py — single source of truth for the gap registry.

Scans the canon for rmk:tbs:* and rmk:open:* remarks and writes
verify/tbs_registry.json with the canonical status map below.
Burn-down verifiers import generate() and assert that their own
contributions are recorded; no verifier owns global counts.

Status map history:
  A277 — registry created (4 closures recorded retroactively)
  A278 — burn-down I (stages 1-2)
  A279 — burn-down II (stage 3)
  A280 — burn-down III (P07 rework -> reworked; P16 -> refuted)
  A281 — observation boundary derived: P007_1/2 reworked -> retired
  A282 — mass-formula provenance: P027_3_c note sharpened
  A283 — alpha-comma mechanism: P000_1_c note sharpened
  A293 — burn-down IV: P015_1 retired, P020_1 refuted, P017_3_c +
         P013_1/2/3_c confirmed-load-bearing with diagnoses
  A295 — pre-publication closure: P018_2 retired, P017_2 + P017_3_c
         refuted (corrected Dirac system: no factor 7), full
         classification — ZERO unreviewed
"""
import os, re, glob, json

HERE = os.path.dirname(os.path.abspath(__file__))
ADD = os.path.dirname(HERE)
TOE = os.path.join(os.path.dirname(ADD), "toe")

RETIRED = {
    # A277 (retroactive recording)
    "P028_2": "A273", "P028_3_c": "A263", "P014_1": "A266",
    "P027_2": "A276",
    # A278 (burn-down I)
    "P031_1_c": "A237", "P015_3_c": "A266/A267",
    "P034_3_c": "A265/A273/A275", "P003_3": "A278", "P029_1": "A278",
    # A279 (burn-down II, stage 3)
    "P014_2": "A279",   # identification false; exact decomposition supplied
    "P018_1": "A279",   # factor-138 identified as mu0 = Omega
    # A281 (boundary condition derived; prose retraction recorded)
    "P007_1": "A281", "P007_2": "A281",
    # A293 (Cauchy repair: result right, intermediate corrected)
    "P015_1": "A293",
    # A295 (normalization conflict = the mu0 factor of A279/A282)
    "P018_2": "A295",
}
SUPERSEDED = {"P019_3": ("P32", "equivalence of routes unproved")}
SCOPE = {"P021_2_c": "permanent honesty marker (Clay scope)"}
for _i in ("P021_1_c","P021_3_c","P022_1_c","P022_2_c","P022_3_c",
           "P023_1_c","P023_2","P023_3_c","P024_2_c","P024_3_c",
           "P025_1_c","P025_2_c","P025_3_c","P026_1_c","P026_2_c",
           "P026_3_c"):
    SCOPE[_i] = ("A295: application-paper defect, recorded; P21-P26 are "
                 "outside the load-bearing spine (package design); repair "
                 "not scheduled")
for _i in ("P008_3_c","P029_2_c","P029_3_c","P030_1"):
    SCOPE[_i] = ("A295: technical/scope note — external anchor or theorem "
                 "hypothesis caveat; corpus use unaffected")
REWORKED = {}
REFUTED = {
    "P016_3": "A280: refuted under all four natural readings (best counts "
              "2/10 vs claimed 14/19; means at/near uniform null); "
              "per-prime mean 0.193 matches claimed 0.187 within 3% — "
              "published table likely mixed readings; first formally "
              "refuted canon claim",
    "P020_1": "A293: v = M_Pl/sqrt(mu0 mu1) = 1.0e17 GeV, off by 4.1e14 — "
              "refuted as stated; flags (unpromoted): ln(v/me) vs E_self "
              "(0.70%), exponent fraction vs 3/4 (0.53%)",
    "P017_2": "A295: no factor-7 gap in the corrected Dirac spectrum "
              "(ratios 1.8-2.6 across all channels/BCs); refuted",
    "P017_3_c": "A293+A295: printed system ill-posed (complex indicial "
                "exponents) AND printed spectrum not recovered by the "
                "natural repair (corrected spectra box-like, spacing ~pi); "
                "refuted as stated; corrected spectra filed",
    "P001_4": "A344: tri-bimaximal theta_13 = 0 refuted; measured "
              "theta_13 ~ 8.5 deg (sin^2 theta_13 ~ 0.022, NuFIT-6.0), "
              "nonzero at >5sigma since Daya Bay 2012; a vanishing reactor "
              "angle is excluded — it is a leading-order angle, not a "
              "perturbation",
    "P001_5": "A344: delta_CP ~ 240 deg refuted on current data; NuFIT-6.0 "
              "(2024, NO) gives delta_CP ~ 177 deg (+19/-20), CP-conserving "
              "within 1sigma, with 240 deg ~3sigma above central; the "
              "claimed agreement used the superseded 197+/-25 fit",
    "P001_6": "A344: topological strong-CP solution refuted (wrong object) "
              "— setting instanton winding n=0 fixes the gauge sector, not "
              "the Lagrangian theta-coupling (the physical strong-CP "
              "parameter); theta_QCD is an independent input, strong CP "
              "remains open, 'no axion needed' does not follow",
}
A295_LB = {
 "P000_3":"A295: R-operator never formally specified — formalization gap",
 "P002_1":"A295: DE/E_self = 0.0464 vs 22k = 0.0469 (1.2%) — conflation "
          "real, numerically benign; derivation gap stands",
 "P003_2":"A295: 5/4 global minimality unproven (local result only)",
 "P007_3":"A295: the WHY of moments-to-hierarchy = the correspondence "
          "postulate (ties to OI-282-1)",
 "P008_1":"A295: dimension not derived from transversality",
 "P008_2_c":"A295: continuum-limit circularity stands",
 "P009_3_c":"A295: triple-point circularity stands",
 "P010_3":"A295: c=3/4 normalization circularity stands",
 "P015_2":"A295: the 4/3 step unexplained",
 "P030_2":"A295: drift bound inconsistency — testable against the GON "
          "implementation; not yet run",
 "P030_3_c":"A295: J3(O) conservation claim structural, unverified",
 "P031_2_c":"A237/A295: d=3,k=4 monodromy case open",
 "P031_3_c":"A237/A295: frame perpendicularity step open",
 "P033_2":"A295: Vol(S^{d-1}) = pi^d confirmed FALSE at every d "
          "(correct: 2 pi^{d/2}/Gamma(d/2)); downstream argument needs "
          "rework",
 "P033_3_c":"A295: Hopf absorption claim structural, unverified",
 "P034_2_c":"A295: associativity proof gap (extrinsic mean) stands",
}
LOADBEARING = {
    "P041_1": "Paper 41 (a-lane): witnessing = coherent bilinear composition "
              "(premise P1); the witness-step = Cayley-Dickson doubling and the "
              "substrate dims {1,2,4,8} follow (BMK/Hurwitz). Rests on the P27 "
              "linear-coherence frame, not a new posit. verify_Paper41.py",
    "P041_2": "Paper 41: U(1)/Maxwell core closed (dF=0 and first-Chern c1=1 "
              "exact on S^3; d*F=cF curvature term derived; flat-limit source-free "
              "Maxwell; coupling alpha). Closes Paper 19's schematic U(1) "
              "reduction. verify_Paper41.py",
    "P000_1_c": "A279+A283: the alpha-comma (2.2234 ppm, ~1.45e4 sigma) "
                "typed as the time-average dressing of P03's oscillation: "
                "deficit = 1-sqrt(1-A^2); A = kappa predicts 2.2745 ppm "
                "(sign right, 2.3% high); inverted: first measurement of "
                "the oscillation amplitude A = 0.9887(2) kappa, "
                "edition-stable; exact amplitude = OI-283-1. A284: "
                "correction bracketed 0.9773(2) — edge exclusion "
                "(1-pi/Omega) vs 1-1/44, identification below bar; "
                "conditional edge prediction alpha^-1 = 137.035999236 "
                "(0.43 ppb from CODATA-22); 0.1-ppb discriminator",
    "P027_3_c": "A279+A282: provenance found — derived in P03 Thm "
                "beta_qed, uncited by P27; 3pi/20 = (1/10)(3pi/2), the "
                "3pi/2 textbook one-loop QED; P27's dressing 1/(1-x) is a "
                "corruption of P03's derived (1+x), which hits measurement "
                "at 5.5e-6 (7x better); remaining: the integer 10 + the "
                "correspondence postulate = OI-282-1",
    "P013_1": "A293: quintic is a schema — C1,C2,C3 and F unspecified; "
              "coefficients uncomputable as stated",
    "P013_2": "A293: degree bound unverifiable until the construction is "
              "supplied (see P013_1)",
    "P013_3_c": "A293: rationality premise unproven and generically false "
                "for Sturm-Liouville eigenvalues; stands as the schema's "
                "deepest gap",
}

def generate(writer="tbs_registry_gen"):
    items = []
    for f in sorted(glob.glob(os.path.join(TOE, "*.tex"))):
        txt = open(f, encoding="utf-8", errors="replace").read()
        for kind, pat in (
            ("TBS", r"\\begin\{remark\}\[TBS\]\\label\{rmk:tbs:([A-Za-z0-9_]+)\}(.*?)\\end\{remark\}"),
            ("Open", r"\\begin\{remark\}\[Open\]\\label\{rmk:open:([A-Za-z0-9_]+)\}(.*?)\\end\{remark\}")):
            for m in re.finditer(pat, txt, re.S):
                items.append({"kind": kind, "id": m.group(1),
                              "paper": os.path.basename(f),
                              "excerpt": " ".join(m.group(2).split())[:200],
                              "status": "unreviewed-or-open",
                              "closed_by": None, "note": None})
    for it in items:
        i = it["id"]
        if i in RETIRED:
            it["status"], it["closed_by"] = "retired", RETIRED[i]
        elif i in SUPERSEDED:
            it["status"] = "superseded"
            it["closed_by"], it["note"] = SUPERSEDED[i]
        elif i in SCOPE:
            it["status"], it["note"] = "scope", SCOPE[i]
        elif i in REWORKED:
            it["status"] = "reworked"
            it["note"] = REWORKED[i]
        elif i in REFUTED:
            it["status"] = "refuted"
            it["note"] = REFUTED[i]
        elif i in LOADBEARING:
            it["status"] = "confirmed-load-bearing"
            it["note"] = LOADBEARING[i]
        elif i in A295_LB:
            it["status"] = "confirmed-load-bearing"
            it["note"] = A295_LB[i]
    counts = {}
    for it in items:
        counts[it["status"]] = counts.get(it["status"], 0) + 1
    payload = {"generated_by": writer, "counts": counts, "items": items}
    # preserve a previously generated VDC section unless regenerating it
    prev = os.path.join(HERE, "tbs_registry.json")
    if os.path.exists(prev):
        try:
            old = json.load(open(prev))
            for k in ("vdc_items", "vdc_counts"):
                if k in old:
                    payload[k] = old[k]
        except Exception:
            pass
    with open(prev, "w") as fh:
        json.dump(payload, fh, indent=1)
    return items, counts


def generate_vdc(writer="tbs_registry_gen"):
    """Verifier-documented claims (VDC): one item per expected-fail
    check in the paper-series verifiers (verify_P000-P034). These are
    claims the verifiers recompute and document as failing, the
    registry's executable counterpart. Extracted by running each
    verifier and parsing its [FAIL] lines; requires the suite to be
    runnable. Stored in a separate section so the remark-derived
    items and counts remain stable for existing consumers."""
    import subprocess
    vdc = []
    for n in range(35):
        vname = "verify_P%03d.py" % n
        vpath = os.path.join(HERE, vname)
        if not os.path.exists(vpath):
            continue
        r = subprocess.run(["python3", vname], cwd=HERE,
                           capture_output=True, text=True, timeout=600)
        out = r.stdout + r.stderr
        k = 0
        for line in out.splitlines():
            ls = line.strip()
            if not ls.startswith("[FAIL]"):
                continue
            k += 1
            body = re.sub(r"^\[FAIL\]\s*\d*\.?\s*", "", ls)
            if " -- " in body:
                claim, note = body.split(" -- ", 1)
            else:
                claim, note = body, None
            texs = glob.glob(os.path.join(TOE, "%02d_*.tex" % n))
            vdc.append({"kind": "VDC",
                        "id": "P%03d_v%02d" % (n, k),
                        "paper": os.path.basename(texs[0]) if texs
                                 else "P%03d" % n,
                        "verifier": vname,
                        "status": "verifier-documented",
                        "claim": claim.strip(),
                        "note": note.strip() if note else None})
    data = json.load(open(os.path.join(HERE, "tbs_registry.json")))
    data["vdc_items"] = vdc
    data["vdc_counts"] = {"verifier-documented": len(vdc)}
    with open(os.path.join(HERE, "tbs_registry.json"), "w") as fh:
        json.dump(data, fh, indent=1)
    return vdc
