# QUINCUNX CIR implementation + single re-execution — report, 10 August 2026

Implementation-and-re-execution lane's report. Applies the CLOSED fix ledger
(`docs/generated/quincunx_cir_fix_ledger_20260810.md`) to
`ops/popper/edr_rivals/`, builds the L9 repaired text column, re-passes the test
suite, and performs the single official re-execution
(`docs/DESIGN_QUINCUNX_CIR_PROTOCOL_20260810.md` condition 16). No git commit. No
self-marking — every disposition below is this lane's record, for the orchestrator's
adjudication, per the same discipline the ledger and its antecedent audits used.

## 1. Pre-repair / post-repair hashes

Every `.py`/`.json`/`.md` file under `ops/popper/edr_rivals/` (excluding
`__pycache__/*.pyc`, which regenerate on import and carry no information beyond the
`.py` source hash):

| File | SHA-256 before | SHA-256 after | Changed? |
|---|---|---|---|
| `analyses.py` | `c0820ebabbf9cb427ce3c3d64ee91adf2f179faa48fabd4a4f8450529cfe6402` | `841fb263367460ef8f33de7c32f3ae3a6e4fff3d203a60deee1684a06266c288` | **YES** (L6, L7, L8, L9, L10, L13) |
| `bindings.py` | `1dde59fbc14a9cb511252f4de89503c898a43d9bf6e73fb939c706dc45b3a324` | `b6be0af1a18f96d4a1ed457fe6958ffea1181baa30a6720becd29cb8e8495ae8` | **YES** (L3, L5) |
| `config.py` | `5e2c64cd475552694afeccf215bc77f995ba77a3635be1d8f8364bc9bd041b15` | `3a9fc1a4689b4bf8dc0b6312cd0cd63a7a7ce74b97966a0568b935e3d2201998` | **YES** (L1, L3, L4, L9-plumbing, L10, L13) |
| `db.py` | `048e212b712457bab8d2642310513bc20c89b1557d8d3ef191165efb5b076ec9` | (same) | No |
| `overlap_audit.py` | `fdf5d3245f79ade8b1083685c1180f52e58beffb1695c03793c3826b53b1dc06` | (same) | No |
| `run_all.py` | `da8fc2fca89beca3b448ea0b409517c00ec29971ab117a6042f2fd6ffafe3660` | (same) | No |
| `fixtures/synthetic_corpus.py` | `3a717f014709a031e27885c6fdc0890554229219ef2f8bc5653731c8484c2fae` | (same) | No |
| `test_analyses.py` | `8c9f830dc1739778cfc4969dfb77743b921211fb1d1a7002c3877bca1a9e2498` | `a0c405efa309b43db5d704dd32318d45b53a1c7ae488cb5045a6beb834d321c0` | **YES** (test coverage for L1/L3/L4/L5/L6/L7/L13) |
| `results/edr_gate5_execution_20260809T211338Z.{json,md}` (alpha's own frozen output) | `29105bc5...`/`ebafd2b3...` | (same) | No — untouched |

**config.py's pre-repair hash (`5e2c64cd...`) exactly matches the frozen bundle's own
disclosed hash** (bundle §8 row A6: "frozen config.py SHA-256 5e2c64cd..."),
independently confirming the starting point was the genuine countersigned baseline
before any edit in this lane began. Exactly the 4 files the ledger's APPLY items
touch changed; `db.py`, `overlap_audit.py`, `run_all.py`, and the fixtures builder are
byte-identical before and after, and the alpha run's own results files were never
touched. `ops/popper/cir_audit_lane_a/test_transport_coercion_roundtrip.py` (outside
this hash scope, a separate lane's directory) was also edited — see §6.

## 2. Diffs applied, verbatim

### 2.1 `config.py`

```diff
diff --git a/ops/popper/edr_rivals/config.py b/ops/popper/edr_rivals/config.py
index b95544c..f44ebb0 100644
--- a/ops/popper/edr_rivals/config.py
+++ b/ops/popper/edr_rivals/config.py
@@ -344,10 +344,26 @@ MIN_DATA_RULES: dict[str, MinDataRule] = {
 #    and with S:6.8's "these corpora lack probability sampling frames"). Holm-Bonferroni
 #    step-down formally requires a per-test p-value the bundle never specifies how to
 #    derive for 8 of these 9. This file therefore implements family BOOKKEEPING
-#    (disclosed alongside every verdict) rather than fabricating a null model. The ONE
-#    place literal Holm/BH correction IS mechanically well-founded is the Rome
-#    triangulation's S:6.9 TOST/CI machinery (proportions with known per-stratum N),
-#    which analyses.py implements properly.
+#    (disclosed alongside every verdict) rather than fabricating a null model.
+#
+#    CORRECTED 2026-08-10 (CIR fix ledger row L10, from the CIR audit's AGG-09
+#    arithmetic reconciliation -- docs/generated/quincunx_cir_audit_laneA_20260810.md
+#    S:7): the Rome triangulation's S:6.9 machinery does NOT apply a Holm/BH family-
+#    size adjustment either -- an earlier version of this comment overstated it as
+#    "implemented properly." What `run_pattern_3` actually does: a FIXED-LEVEL TOST
+#    (independent 90% CI-inside-+-2.0pp-margin test, config.TOST_CI_LEVEL/
+#    EQUIVALENCE_MARGIN_PP) evaluated separately PER COMPARISON, across exactly NINE
+#    pairwise comparisons (estimand 1's 3 corpus-pairs, plus estimand 2's identical
+#    3-pairwise-comparison block run TWICE -- once per funerary/non-funerary arm --
+#    3 + 6 = 9), not the "up to 6" this section's own S:6.9 citation and
+#    TRIANGULATION_FAMILY_MAX_TESTS below once implied. No step-down, no alpha-
+#    spending, and no other family-size correction is applied to those 9 comparisons'
+#    significance level -- each is tested at the same fixed 90%/95% CI level on its
+#    own. HOLM_FAMILYWISE_ALPHA, BH_ADVISORY_Q, and TRIANGULATION_FAMILY_MAX_TESTS
+#    below remain DISCLOSURE-ONLY constants (surfaced via `family_context()` /
+#    `run_pattern_3`'s own output for the countersignatory's benefit) -- neither is
+#    read by any comparison, threshold, or verdict computation anywhere in this
+#    package (confirmed by the CIR audit's own call-graph trace, AGG-09/LUT-08).
 # ---------------------------------------------------------------------------
 
 HOLM_FAMILYWISE_ALPHA = 0.05
@@ -468,6 +484,25 @@ EDR_BRACKET_PRESENCE_COLUMN = "has_bracket_markup"
 EDR_GAP_COUNT_PATH = "raw->'gap_count'"
 EDR_SUPPLIED_COUNT_PATH = "raw->'supplied_count'"
 
+#: CIR fix ledger row L9 (2026-08-10, A0 -- the substantive repair of this
+#: replication): a real top-level text column (NOT a raw->> jsonb path), added by
+#: ops/popper/cir_repair/build_edition_text_leiden.py, re-rendered from the frozen
+#: source XML under /srv/mirrors/edr_zenodo_v12/extracted/ for all 105,222 eligible
+#: rows. Rendering rule (spec-determined, EpiDoc + Leiden convention): within
+#: div[@type='edition'], supplied[@reason] content -> "[content]"; gap elements ->
+#: a fixed "[------]" marker; all other text exactly as the alpha extraction (the
+#: raw->>'edition_text' column, left untouched beside this one) already rendered it.
+#: The alpha extraction's own bug (confirmed by direct reconstruction): plain
+#: ElementTree text-node flattening with NO markup awareness at all -- <gap> (always
+#: empty) contributed nothing (invisible loss), <supplied> contributed its restored
+#: text completely unmarked (indistinguishable from directly-attested text) --
+#: violating the registered lossless-ingestion premise. Consumed by 18-codex-r2 only
+#: (the only live rival reading edition text); 18-grok-r2 reads gap_count/
+#: supplied_count directly (structured, unaffected by this column) but is itself
+#: CIR-parked (L13, Sec16 below) this run regardless. Build+verification record:
+#: docs/generated/quincunx_cir_rerun_report_20260810.md.
+EDR_EDITION_TEXT_LEIDEN_COLUMN = "edition_text_leiden"
+
 #: (B) PROPOSED gate-5 binding (candidate log row: bracket-density definition). The
 #: bundle names "bracket density" (S:2.8) without a closed-form definition and without
 #: giving me the base conjecture's own upstream EDH bracket-density computation to
@@ -551,6 +586,28 @@ EAGLE_MATERIAL_STONE_LOD_IDS = frozenset({
     16, 30, 31, 80,               # basalt, vulkanischer Tuff, pumice, Pyroclastische Sedimente (lod/80 -- NOT travertine; verified)
     75, 73, 86, 104,              # sandstone, breccia, flint, quartz
     2, 1,                         # (local) stone, rock
+    # CIR fix ledger row L1 (2026-08-10, docs/generated/quincunx_cir_fix_ledger_20260810.md
+    # APPLY; diff per Lane A audit LUT-12,
+    # docs/generated/quincunx_cir_audit_laneA_20260810.md): EAGLE-hierarchy-confirmed
+    # stone/marble ids, each proven via the vocabulary's own "Contained in" parent
+    # declaration resolving to an id already registered above.
+    57,                  # Kalksandstein (calcareous sandstone) -- child of 60-family, EAGLE-confirmed stone
+    82,                  # Peperin -- "Contained in: Pyroclastische Sedimente" (=80, already registered)
+    365,                 # Marmor, numidischer (yellow marble) -- "Contained in: Marmor, geaedert/farbig" (=50, already registered)
+    736,                 # Grauwacke -- "Contained in: Schiefer" (=52, already registered)
+    739, 743, 744, 745,  # Marmor Chium / parischer / phrygischer / prokonnesischer -- all "Contained in: Marmor" (=48, already registered)
+    # NOT added -- Lapislazuli/93: ledger row L2 (A1, NO CHANGE; parent "Minerale" is
+    # not itself a registered id, no direct hierarchy chain, disclosed, 1 record).
+    # FLAGGED, not resolved here -- Marmor-taenarischer/747 (1 record): Lane A's LUT-12
+    # census lists it among the "10 real stone/marble materials misclassified" with the
+    # same "Contained in: Marmor"=48 chain as 739/743/744/745, and both the ledger's own
+    # finding text ("9 missing stone lod-ids") and Lane A's "A0 proof for 9 of 10" count
+    # 9 -- but neither the ledger's explicit numeric list nor its diff actually names
+    # 747. Applied here exactly as the ledger enumerates (8 ids, matching its literal
+    # decision text); 747 reported as an open discrepancy for the orchestrator in
+    # docs/generated/quincunx_cir_rerun_report_20260810.md, not silently added or
+    # dropped. Zero live-path effect either way (material classification feeds only the
+    # now-parked 09-grok-r1, L13/Sec16 below).
 })
 #   Countersignature amendment C2 (2026-08-09), per-id verified against the EAGLE
 #   material lod pages: 48 general Marmor ADDED (the omission mattered doubly --
@@ -563,9 +620,15 @@ MATERIAL_STONE_TEXT_FALLBACK = (
     "marmo", "marble", "kalkstein", "limestone", "calcare", "travertin",
     "alabaster", "granit", "granite", "basalt", "tuff", "tufo", "sandstein",
     "sandstone", "arenaria", "breccia", "pietra", "stein", "stone", "rock",
+    "lapis", "saxum",
 )
 #   C2: "marmor" -> "marmo" stem (catches Italian marmo AND German Marmor);
 #   Italian stems pietra/calcare/tufo/arenaria added (EDR is an Italian corpus).
+#   CIR fix ledger row L4 (2026-08-10, A0/A1 boundary -- classified APPLY): "lapis",
+#   "saxum" added -- classical Latin generic words for "stone," absent from the prior
+#   IT/EN/DE-only list despite EDR's material-text field frequently being scholarly
+#   Latin. Resolves 1,174 of 1,403 unmatched blank-ref records (Lane A audit LUT-13,
+#   docs/generated/quincunx_cir_audit_laneA_20260810.md).
 # Everything else EAGLE-classified (metal, fired-clay/ceramic, organic/perishable,
 # plaster, mosaic, glass, paper, textile) counts as non-stone/perishable per the
 # rival's own contrast (stone vs. "non-stone or poorly surviving media").
@@ -576,7 +639,32 @@ MATERIAL_NONSTONE_TEXT_FALLBACK = (
     "lignum", "wood", "cera", "wax", "elfenbein", "ivory", "bone", "knochen",
     "papyrus", "leder", "leather", "tectorium", "plaster", "arenatum",
     "musivum", "mosaic", "vitrum", "glass", "papier", "paper", "stoff", "textile",
+    "eburneus",
 )
+#   CIR fix ledger row L4 (2026-08-10, A0/A1 boundary -- classified APPLY): "eburneus"
+#   added -- classical Latin for "ivory" (the prior list had "ivory"/"elfenbein" but
+#   not the Latin form). Resolves 140 blank-ref records (Lane A audit LUT-13).
+#   CIR fix ledger row L5 (2026-08-10, A0): "tin" is matched via a WORD-BOUNDARY regex
+#   in bindings.classify_material, not bare substring containment -- see that
+#   function's own comment. The bare string is too short for this multilingual corpus
+#   (collides with "Tiburtinus"/"triestina", genuine travertine-stone place-names
+#   containing "tin" as a coincidental infix; 33 records, Lane A audit LUT-13). Every
+#   OTHER candidate in this tuple still matches by plain substring.
+#
+# CIR fix ledger row L3 (2026-08-10, A0 -- the authority's own six-language labels
+# entail non-material): EAGLE material lod ids 118 ("ceterum"/other) and 138
+# ("ignoratur"/unknown) mean "material not recorded," not a material identity --
+# `classify_material`'s prior "any other confirmed id -> matched non-stone"
+# short-circuit silently absorbed both as non-stone (10,791 eligible records, 10.25%
+# of the eligible population -- the single largest-magnitude finding in the CIR
+# audit, Lane A LUT-12 cont.). Checked BEFORE that fallback in
+# bindings.classify_material; both ids now return matched=False (the disclosed
+# "other"/indeterminate bucket), not matched-non-stone. Bounded consequence, traced
+# by Lane A: classify_material/is_non_stone_or_perishable is called from exactly one
+# place package-wide (analyze_09_grok_r1, itself now CIR-parked by L13/Sec16 below,
+# so this fix has zero live-path effect this run either -- applied for the record and
+# future runs, per the ledger).
+EAGLE_MATERIAL_INDETERMINATE_LOD_IDS = frozenset({118, 138})
 
 # ---------------------------------------------------------------------------
 # 12. Language binary rule (bundle S:6.3) -- BINDS-AT-STAGING, resolved here via the
@@ -798,3 +886,34 @@ REGION_INVENTORY_PARKS: dict[str, str] = {
     "12-grok-r1": "bundle S:8 row P1: same region-inventory park (Raetia/Noricum, 0 rows).",
 }
 ALL_PARKED_RIVAL_IDS: tuple[str, ...] = tuple(MECHANICAL_PARKS) + tuple(REGION_INVENTORY_PARKS)
+
+# ---------------------------------------------------------------------------
+# 16. CIR validation-gate parks (2026-08-10) -- fix ledger row L13, gates doc
+#     docs/generated/quincunx_cir_gates_20260810.md. Distinct from sections above:
+#     these three ARE registered live rivals with real thresholds/d_dep/min-data
+#     rules (unlike MECHANICAL_PARKS/REGION_INVENTORY_PARKS, which were never live
+#     rivals at all) -- they PARK in THIS re-execution specifically because the CIR
+#     audit's gold-label validation frame found their governing proxy invalid:
+#     PXY-01 (genre, sensitivity/specificity LCB99 fails 0.95) and PXY-02 (material,
+#     negative-class floor + sensitivity LCB99 both fail) for 09-grok-r1's
+#     material-x-genre share; PXY-01 for 09-grok-r2's genre-plurality share; PXY-06
+#     (gap/supplied -> loss/restored, sensitivity 0.60 against a 0.95 bar) for
+#     18-grok-r2's physical-loss/formulaic proxy. The ALPHA VERDICT remains the
+#     published record for each of the three; this park applies ONLY to the
+#     corrected-instrument re-execution, per protocol condition 12 ("no tuning-
+#     until-pass") -- the corrected-instrument delta for these three is DEFERRED to a
+#     future frame with repaired evidence.
+#
+#     NOT added to MECHANICAL_PARKS/REGION_INVENTORY_PARKS/ALL_PARKED_RIVAL_IDS --
+#     those enumerate a structurally different, PRE-REGISTERED category that
+#     analyses.parked_rival_reports() reports separately from the 9 live-rival slots.
+#     These three stay inside LIVE_ANALYSES/LIVE_RIVAL_IDS (they are still nominally
+#     "live" registration-wise -- only THIS run's validation-gate outcome parks them)
+#     and instead short-circuit at the top of their own analyze_* function in
+#     analyses.py, returning a VerdictResult with verdict=
+#     VERDICT_PARKED_BY_VALIDATION_GATE, no computed quantities, and d_dep carried.
+# ---------------------------------------------------------------------------
+
+VERDICT_PARKED_BY_VALIDATION_GATE = "PARKED-BY-VALIDATION-GATE (gold-label gate failed)"
+
+VALIDATION_GATE_PARKED_TEST_IDS = frozenset({"09-grok-r1", "09-grok-r2", "18-grok-r2"})
```

### 2.2 `bindings.py`

```diff
diff --git a/ops/popper/edr_rivals/bindings.py b/ops/popper/edr_rivals/bindings.py
index 1a03c9b..121073d 100644
--- a/ops/popper/edr_rivals/bindings.py
+++ b/ops/popper/edr_rivals/bindings.py
@@ -169,12 +169,29 @@ def is_genre_populated(ref: Optional[str], text: Optional[str]) -> bool:
 # ---------------------------------------------------------------------------
 
 
+#: CIR fix ledger row L5 (2026-08-10, A0): "tin" (metal tin/German Zinn) is too short
+#: a substring for this multilingual corpus -- it coincidentally matches inside
+#: "Tiburtinus"/"tibutinus"/"triestina" (place/quarry names for travertine, genuine
+#: stone), misclassifying 33 records (Lane A audit LUT-13). Matched via a word-
+#: boundary regex instead of bare substring containment in classify_material below;
+#: every other MATERIAL_NONSTONE_TEXT_FALLBACK candidate is unaffected (still plain
+#: substring).
+_TIN_WORD_RE = re.compile(r"\btin\b")
+
+
 def classify_material(ref: Optional[str], text: Optional[str]) -> dict:
     """Returns {'stone': bool, 'non_stone': bool, 'matched': bool}."""
     lod_id = _extract_lod_id(ref)
     if lod_id is not None:
         if lod_id in config.EAGLE_MATERIAL_STONE_LOD_IDS:
             return {"stone": True, "non_stone": False, "matched": True}
+        # CIR fix ledger row L3 (2026-08-10, A0): EAGLE meta-ids 118 ("ceterum") and
+        # 138 ("ignoratur") mean "material not recorded/other," not a material
+        # identity (confirmed via the vocabulary's own six-language labels) --
+        # checked BEFORE the "any other confirmed id" fallback below, so they return
+        # the disclosed indeterminate/unmatched bucket instead of matched-non-stone.
+        if lod_id in config.EAGLE_MATERIAL_INDETERMINATE_LOD_IDS:
+            return {"stone": False, "non_stone": False, "matched": False}
         # Any OTHER lod id under the material vocabulary (confirmed-namespace ref
         # present, but not on our stone list) is treated as matched-non-stone: the
         # EAGLE material vocabulary's remaining terms are metal/ceramic/organic/etc.,
@@ -184,8 +201,12 @@ def classify_material(ref: Optional[str], text: Optional[str]) -> dict:
     if low:
         if any(cand in low for cand in config.MATERIAL_STONE_TEXT_FALLBACK):
             return {"stone": True, "non_stone": False, "matched": True}
-        if any(cand in low for cand in config.MATERIAL_NONSTONE_TEXT_FALLBACK):
-            return {"stone": False, "non_stone": True, "matched": True}
+        for cand in config.MATERIAL_NONSTONE_TEXT_FALLBACK:
+            if cand == "tin":
+                if _TIN_WORD_RE.search(low):
+                    return {"stone": False, "non_stone": True, "matched": True}
+            elif cand in low:
+                return {"stone": False, "non_stone": True, "matched": True}
     return {"stone": False, "non_stone": False, "matched": False}
```

### 2.3 `analyses.py`

```diff
diff --git a/ops/popper/edr_rivals/analyses.py b/ops/popper/edr_rivals/analyses.py
index 825ef6d..c5479b2 100644
--- a/ops/popper/edr_rivals/analyses.py
+++ b/ops/popper/edr_rivals/analyses.py
@@ -20,10 +20,23 @@ derive one for 8 of these 9. This module does NOT fabricate a null model -- it r
 each verdict standalone, tagged with its family's registered size / tested members /
 parked members (see `family_context`), so the countersignatory sees the same
 multiplicity context Holm would have consumed, without inventing a number that isn't
-there. The ONE place literal Holm/BH correction has genuine footing -- the Rome
-triangulation's TOST/CI machinery (proportions with known per-stratum N) -- IS
-implemented properly, in `run_pattern_3`. See the gate-5 freeze report for the full
-discussion and the PROPOSED resolution.
+there.
+
+CORRECTED 2026-08-10 (CIR fix ledger row L10, docstring-only -- no computational
+change): the Rome triangulation's TOST/CI machinery in `run_pattern_3` does NOT
+implement Holm/BH correction either, "properly" or otherwise -- an earlier version of
+this docstring overstated it. What `run_pattern_3` actually does: a FIXED-LEVEL TOST
+per comparison (independent 90% CI-inside-margin test, no per-test alpha adjustment),
+run across exactly NINE pairwise comparisons (estimand 1: edh_vs_edb/edh_vs_edr/
+edb_vs_edr = 3; estimand 2: the identical 3-comparison block run once per funerary/
+non_funerary arm = 6; 3+6=9) -- not the "up to 6" `TRIANGULATION_FAMILY_MAX_TESTS`
+names. `HOLM_FAMILYWISE_ALPHA`/`BH_ADVISORY_Q`/`TRIANGULATION_FAMILY_MAX_TESTS`
+(config.py Sec7) remain disclosure-only constants, consumed by no comparison or
+verdict computation anywhere in this module (independently confirmed by the CIR
+audit's AGG-09 arithmetic reconciliation and call-graph trace, LUT-08/AGG-09 --
+docs/generated/quincunx_cir_audit_laneA_20260810.md). See the gate-5 freeze report for
+the original discussion and docs/generated/quincunx_cir_fix_ledger_20260810.md row L10
+for this correction.
 """
 
 from __future__ import annotations
@@ -94,6 +107,30 @@ def _park_result(test_id: str, reason: str, sql_log: Optional[list] = None) -> V
     )
 
 
+def _validation_gate_park_result(test_id: str) -> VerdictResult:
+    """CIR fix ledger row L13 (2026-08-10): this test's governing proxy failed its
+    registered gold-label validation gate in the CIR audit
+    (docs/generated/quincunx_cir_gates_20260810.md) and PARKS in this re-execution --
+    "instead of computing" per the ledger, i.e. this is called BEFORE the caller's own
+    function issues any query. computed_quantities is empty (nothing was computed);
+    d_dep is still carried (disclosure, via _park_result's existing config.
+    D_DEP_GOVERNING/D_DEP_ALTERNATIVE lookups, unchanged); the alpha verdict remains
+    the published record for this test -- the corrected-instrument delta for it is
+    DEFERRED to a future frame with repaired evidence, per protocol condition 12."""
+    result = _park_result(
+        test_id,
+        "PARKED-BY-VALIDATION-GATE: this test's governing proxy failed its registered "
+        "gold-label validation gate in the CIR audit -- see "
+        "docs/generated/quincunx_cir_gates_20260810.md and fix-ledger row L13 "
+        "(docs/generated/quincunx_cir_fix_ledger_20260810.md). The alpha verdict "
+        "remains the published record for this test; the corrected-instrument delta "
+        "for it is DEFERRED to a future frame with repaired evidence, per protocol "
+        "condition 12 (no tuning-until-pass).",
+    )
+    result.verdict = config.VERDICT_PARKED_BY_VALIDATION_GATE
+    return result
+
+
 def family_context(test_id: str) -> dict[str, Any]:
     fam_id = config.TEST_TO_FAMILY.get(test_id)
     fam = config.FAMILIES.get(fam_id) if fam_id else None
@@ -163,10 +200,32 @@ def _edh_matched_province_rows(select_cols: str, where_extra: str = "") -> list[
 
 
 def _as_int(value: Any) -> Optional[int]:
+    """CIR fix ledger row L7 (2026-08-10, A0 -- registered lossless-transport
+    requirement; Lane A audit SER-05/COE-05): robust numeric coercion. Handles a
+    plain integer-string (the only shape previously accepted), a decimal-formatted
+    jsonb number (e.g. '5.0', single- or double-arrow text form -- the arrow operator
+    doesn't matter, the defect was always in the cast, not the arrow), and a
+    single-arrow-selected jsonb STRING value, which serializes with its quotes
+    intact (literal '"5"') unlike a jsonb number, which never carries quotes.
+    Confirmed empirically inert on the current frozen data (100% of eligible
+    gap_count/supplied_count are plain non-decimal jsonb numbers) -- fixed
+    defensively regardless, since this helper is shared by every numeric coercion
+    site in this module, not only the two SER-05/COE-05-named fields."""
     if value is None or value == "":
         return None
+    s = str(value).strip()
+    if not s:
+        return None
+    if len(s) >= 2 and s[0] == '"' and s[-1] == '"':
+        s = s[1:-1].strip()
+        if not s:
+            return None
+    try:
+        return int(s)
+    except (ValueError, TypeError):
+        pass
     try:
-        return int(str(value).strip())
+        return int(round(float(s)))
     except (ValueError, TypeError):
         return None
 
@@ -547,6 +606,8 @@ def analyze_06_codex_r3() -> VerdictResult:
 
 def analyze_09_grok_r1() -> VerdictResult:
     test_id = "09-grok-r1"
+    if test_id in config.VALIDATION_GATE_PARKED_TEST_IDS:  # CIR fix ledger row L13.
+        return _validation_gate_park_result(test_id)
     edr_rows = _edr_rows(
         f"{config.EDR_MATERIAL_FIELD_PATH}->>'ref' AS mref, {config.EDR_MATERIAL_FIELD_PATH}->>'text' AS mtext, "
         f"{config.EDR_GENRE_FIELD_PATH}->>'ref' AS gref, {config.EDR_GENRE_FIELD_PATH}->>'text' AS gtext",
@@ -635,6 +696,8 @@ def analyze_09_grok_r1() -> VerdictResult:
 
 def analyze_09_grok_r2() -> VerdictResult:
     test_id = "09-grok-r2"
+    if test_id in config.VALIDATION_GATE_PARKED_TEST_IDS:  # CIR fix ledger row L13.
+        return _validation_gate_park_result(test_id)
     edr_rows = _edr_rows(f"{config.EDR_GENRE_FIELD_PATH}->>'ref' AS gref, {config.EDR_GENRE_FIELD_PATH}->>'text' AS gtext")
     sql_log = [{"purpose": "EDR per-record genre classification input", "sql": "genre_term.ref/.text WHERE eligible"}]
     grouped = _group_by_matched_province(edr_rows)
@@ -782,9 +845,21 @@ def analyze_14_codex_r2() -> VerdictResult:
         if not adequate:
             return config.VERDICT_INDETERMINATE_MIXED, peaks
         shifted = sum(1 for p in adequate.values() if p not in config.HABIT_PEAK_CENTURY_BUCKETS)
-        if shifted > len(adequate) / 2:
+        # CIR fix ledger row L6 (2026-08-10, A0 -- conformance to registration): the
+        # bundle's own notes field registers REFUTES "for a majority" staying in
+        # 1st/2nd c.; this code previously required unanimity (shifted == 0), a
+        # genuine prose/code mismatch caught by the CIR audit (Lane A LUT-16,
+        # countersignature) -- contrast 09-grok-r1's own notes field, which correctly
+        # says "ALL" for the identical code pattern there. Corrected to the registered
+        # majority rule: FAVORS iff a majority of adequate provinces shifted, REFUTES
+        # iff a majority stayed, exact tie (only possible when len(adequate) is even)
+        # -> INDETERMINATE (mixed) -- the registered A21-style completion. Alpha landed
+        # 13/14 shifted (outside the divergence band), so no alpha-run verdict is
+        # affected by this correction.
+        half = len(adequate) / 2.0
+        if shifted > half:
             return config.VERDICT_FAVORS_PARTIALLY, peaks
-        if shifted == 0:
+        if shifted < half:
             return config.VERDICT_REFUTES_WITHIN_SCOPE, peaks
         return config.VERDICT_INDETERMINATE_MIXED, peaks
 
@@ -836,8 +911,18 @@ def analyze_14_codex_r2() -> VerdictResult:
 
 def analyze_18_codex_r2() -> VerdictResult:
     test_id = "18-codex-r2"
-    edr_rows = _edr_rows(f"raw->>'edition_text' AS edition_text, {config.EDR_GAP_COUNT_PATH} AS gap_count, {config.EDR_SUPPLIED_COUNT_PATH} AS supplied_count")
-    sql_log = [{"purpose": "EDR per-record edition text + structured gap/supplied counts", "sql": "edition_text, gap_count, supplied_count WHERE eligible"}]
+    # CIR fix ledger row L9 (2026-08-10, A0 -- the substantive repair of this
+    # replication): reads the repaired edr_staging.edition_text_leiden column
+    # (config.EDR_EDITION_TEXT_LEIDEN_COLUMN), re-rendered from the frozen source XML
+    # per the EpiDoc/Leiden convention (supplied[@reason] -> [content], gap ->
+    # [------]) by ops/popper/cir_repair/build_edition_text_leiden.py. The alpha
+    # column (raw->>'edition_text') dropped restored/lost spans entirely -- see
+    # ledger row L9 and docs/generated/quincunx_cir_rerun_report_20260810.md for the
+    # build+verification record. Aliased to "edition_text" so the rest of this
+    # function (and the EDH-method-matched '[' check below) is unchanged. EDH side is
+    # UNCHANGED per the ledger ("EDH side unchanged").
+    edr_rows = _edr_rows(f"{config.EDR_EDITION_TEXT_LEIDEN_COLUMN} AS edition_text, {config.EDR_GAP_COUNT_PATH} AS gap_count, {config.EDR_SUPPLIED_COUNT_PATH} AS supplied_count")
+    sql_log = [{"purpose": "EDR per-record Leiden-repaired edition text (CIR ledger L9) + structured gap/supplied counts", "sql": "edition_text_leiden AS edition_text, gap_count, supplied_count WHERE eligible"}]
     grouped = _group_by_matched_province(edr_rows)
     grouped.pop("_unassigned_count", 0)
 
@@ -925,7 +1010,14 @@ def analyze_18_codex_r2() -> VerdictResult:
 
 def analyze_18_grok_r2() -> VerdictResult:
     test_id = "18-grok-r2"
-    edr_rows = _edr_rows(f"has_image, {config.EDR_GAP_COUNT_PATH} AS gap_count, {config.EDR_SUPPLIED_COUNT_PATH} AS supplied_count")
+    if test_id in config.VALIDATION_GATE_PARKED_TEST_IDS:  # CIR fix ledger row L13.
+        return _validation_gate_park_result(test_id)
+    # CIR fix ledger row L8 (2026-08-10, A0 cosmetic -- Lane A audit FLD-13): named
+    # constant restored at this call site (was the bare literal "has_image"). Zero
+    # behavior change -- config.EDR_IMAGE_PRESENCE_COLUMN == "has_image". Unreachable
+    # this run (this test is CIR-parked, L13 above), applied for the record and any
+    # future re-validated run.
+    edr_rows = _edr_rows(f"{config.EDR_IMAGE_PRESENCE_COLUMN}, {config.EDR_GAP_COUNT_PATH} AS gap_count, {config.EDR_SUPPLIED_COUNT_PATH} AS supplied_count")
     sql_log = [{"purpose": "EDR per-record image presence + structured gap/supplied counts", "sql": "has_image, gap_count, supplied_count WHERE eligible"}]
     grouped = _group_by_matched_province(edr_rows)
     grouped.pop("_unassigned_count", 0)
```

### 2.4 `test_analyses.py` and Lane A's `test_transport_coercion_roundtrip.py`

Full diffs (251 and 44 lines respectively) omitted here for length — reproduced in
full in §6 below alongside the test-by-test rationale. Reproducible directly:
`git diff ops/popper/edr_rivals/test_analyses.py
ops/popper/cir_audit_lane_a/test_transport_coercion_roundtrip.py`.

## 3. L1 discrepancy — flagged, not resolved (see also delta report §4.4)

The ledger's L1 row states the finding as "9 missing stone lod-ids" but its own
explicit decision text lists 8: `57, 82, 365, 736, 739, 743, 744, 745`. Lane A's
LUT-12 census independently names a 9th structurally-identical case —
Marmor-taenarischer/lod-747 (1 record, "Contained in: Marmor"=48, the identical
parent chain as 739/743/744/745) — that is absent from both the ledger's inline list
and its diff block, distinct from Lapislazuli/93 (which the ledger explicitly and
correctly excludes via row L2, NO CHANGE). This implementation applied the ledger's
literal 8-id enumeration exactly as written, per the binding instruction to
"implement exactly its APPLY items… and NOTHING else," rather than unilaterally
resolving the apparent 9th-id gap myself. Zero live-run consequence either way:
material classification feeds only `analyze_09_grok_r1`, which is independently
CIR-parked by L13 this run (config.py §16), so this discrepancy affects no computed
quantity, threshold distance, or verdict in this execution — only the disclosure-only
stone-id set, applied "for the record and future runs" per the ledger's own L1
annotation.

## 4. L9 — building `edr_staging.edition_text_leiden`

New script: `ops/popper/cir_repair/build_edition_text_leiden.py`. Reverse-engineered
the alpha extraction first (confirmed exact reproduction against a live sample,
id=6303/EDR016161, gap_count=1/supplied_count=2): `"".join(div_elem.itertext()).strip()`
for the single `div[@type='edition']` element — plain ElementTree text-node
flattening, no markup awareness; `<gap>` (empty) contributes nothing, `<supplied>`
contributes its inner text completely unmarked. The repair renders the same
document-order flattening but recognizes `<gap>` → a fixed `[------]` marker and
`<supplied reason="...">` → its own (recursively-rendered) content wrapped in one
`[...]` pair, recursing through the identical function so nesting anywhere in the
subtree is handled; every other node renders exactly as the alpha extraction already
does.

**Pre-build probe (3,000 random eligible records)**, before committing to the design,
confirmed 100%: exactly one `div[@type='edition']` per record; `gap_count`/
`supplied_count` equal the exact `<gap>`/`<supplied>` tag counts found *only* inside
that div; every single `<supplied>` tag in the sample already carries a `@reason`
attribute (the `[@reason]` predicate is a no-op safety net on this corpus, kept
because the ledger's rendering rule states it explicitly).

**Build run** (full population, no `--limit`, real write):

```
[1/4] ALTER TABLE edr_staging ADD COLUMN IF NOT EXISTS edition_text_leiden text  -- done.
[2/4] 105222 eligible rows fetched (== EDR_ELIGIBLE_COUNT).
[3/4] Rendering + writing in batches (500 rows/batch, 211 batches):
      105222/105222 processed, 0 errors. Total elapsed: 513.2s (~8.6 min).
      Structural self-consistency (full population, not just the 3,000-sample):
        records with edition_divs != 1: 0
        gap_count mismatches (recount vs. raw->gap_count): 0
        supplied_count mismatches (recount vs. raw->supplied_count): 0
        total <gap> tags found: 204,052
        total reason-bearing <supplied> tags found: 173,245
        new-column records containing '[' at all: 59,190
[4/4] Verification:
```

**Verification (a) — row count updated**: `SELECT count(*) FROM edr_staging WHERE
eligible AND edition_text_leiden IS NOT NULL` → **105,222 = EDR_ELIGIBLE_COUNT
exactly. PASS.**

**Verification (b) — the 750 `pxy05_edr` gold-sample agreement rate**: crude `'['`
presence in the new column vs. the frozen `packet_index_pxy05_edr.json`
`machine_label.structured_bracket` flag (the same 750-id stratified sample the PXY-05
gate validated):

| | Count |
|---|---:|
| ids compared | 750 / 750 |
| **agreement (new `[` presence == structured_bracket)** | **750 / 750 = 100.00%** |
| both True | 600 |
| both False | 150 |
| disagreements | **0** |
| moved False→True vs. the OLD `crude_bracket` flag (expected repair direction) | 250 |
| moved True→False vs. the OLD flag (would be a red flag — repair should only ADD brackets) | **0** |

The instruction anticipated the new column "should now **largely** coincide" with
`structured_bracket`; the actual result on this 750-id gold sample is **exact**
coincidence (100.00%, 0 disagreements), with the repair moving exactly the 250
previously-mismatched ids in the expected direction and zero ids in the wrong
direction. A companion, corpus-wide (not sample) cross-check (see the delta report
§4.3) found a small, fully-explained, pre-existing residual elsewhere in the
population: 16 of 105,222 eligible records (0.0152%) show a literal `[` in the new
column despite `gap_count=supplied_count=0` — confirmed via direct query that the
identical literal bracket already existed in the untouched alpha `edition_text`
column for all 16 (scribal-erasure/plain-typed brackets in running prose, unrelated
to `<supplied>`/`<gap>` markup) — zero effect from this repair. Zero records show the
opposite gap (structured=True, new bracket=False) anywhere in the full 105,222-row
population.

**Verification (c)**: no per-province density, share, or rate was computed, printed,
or logged by the build script or in this report section — confirmed by direct
reading of the script's own output above (structural/mechanical counts only) and by
this document's own text.

## 5. L9 wiring — `18-codex-r2`'s SQL

`analyze_18_codex_r2` now selects `config.EDR_EDITION_TEXT_LEIDEN_COLUMN AS
edition_text` (real column) in place of `raw->>'edition_text' AS edition_text`
(jsonb path) — a one-line change (§2.3 above), aliased so every downstream line of
the function (the `'[' in edition_text` crude-method computation, the structured
diagnostic, the per-province density dicts) is untouched. EDH-side SQL is
byte-identical, per the ledger's explicit "EDH side unchanged."

## 6. Test suite

**Frozen package's own `test_analyses.py`**: 52 → **61 tests, 61/61 passed**
(`python3 test_analyses.py`, its own built-in runner). 9 new tests added (one per
ledger item touching a live-reachable code path: L1, L3, L4, L5, L6×2, L7, L13×2); 6
existing tests updated (wrapped, not rewritten) because L13 changed the behavior they
exercise.

**Lane A's audit tests** (outside the frozen package, `ops/popper/cir_audit_lane_a/`):
`test_output_formatting.py` **20/20 passed, unchanged** (pure-synthetic
`VerdictResult` shape/JSON/Markdown tests — touches nothing this ledger changed).
`test_transport_coercion_roundtrip.py` **81/81 passed** (throwaway venv with
`psycopg[binary]`, connected to the db container's bridge IP, mirroring Lane A's own
documented method) — **4 of the 81 checks needed updating**, all in the SER-05/COE-05
and COE-01 sections, because they hard-coded the *pre-fix* L7 hazard as the expected
result.

**Total: 61 + 81 + 20 = 162 checks, 162/162 passing.**

### 6.1 Tests updated because the ledger changed the behavior they test

| File | Test(s) | Why updated |
|---|---|---|
| `test_analyses.py` | `test_09_grok_r1_parks_below_pooled_nonstone_50`, `test_09_grok_r1_favors_hand_verified`, `test_09_grok_r2_parks_below_pooled_50`, `test_09_grok_r2_refutes_hand_verified`, `test_18_grok_r2_parks_below_pooled_50`, `test_18_grok_r2_favors_hand_verified` | L13 makes these 3 functions short-circuit to PARKED-BY-VALIDATION-GATE unconditionally, before any DB query — the tests' own FakeDb fixtures would otherwise be dead code and their original assertions would fail. **Wrapped, not rewritten**: added a new `_gate_lifted(test_id)` context manager that temporarily excludes one test_id from `config.VALIDATION_GATE_PARKED_TEST_IDS` so each test's original hand-computed fixture still exercises and verifies the *underlying computation logic* (unreachable this run, still correct source, kept for any future re-validated run) — the original assertions are otherwise untouched. |
| `ops/popper/cir_audit_lane_a/test_transport_coercion_roundtrip.py` | `test_ser05_coe05_gap_count_jsonb_coercion_hazard` (scenarios 2, 3, and the double-arrow contrast check), `test_coe01_as_int_boundaries` (`"42.0"`, `'"42"'` cases) | These hard-coded the *pre-fix* behavior (`_as_int` returning `None` for decimal-formatted numbers and single-arrow jsonb strings) as "CONFIRMED HAZARD." L7 fixes exactly this. Updated to assert the post-fix correct values (`5`/`42` instead of `None`), with the original hazard finding preserved in the surrounding comment/label text (not deleted), and a new assertion confirming the fix is arrow-agnostic (both single- and double-arrow text forms of the same decimal value now parse identically). |

### 6.2 New tests added (one section per ledger item)

| Ledger item | New test(s) in `test_analyses.py` | What it proves |
|---|---|---|
| L1 | `test_classify_material_L1_new_eagle_hierarchy_stone_ids` | All 8 newly-added stone ids classify stone; Lapislazuli/93 unchanged (still non-stone, per L2 NO CHANGE). |
| L3 | `test_classify_material_L3_indeterminate_meta_ids` | lod 118/138 → `matched=False`; a genuinely-other id is unaffected. |
| L4 | `test_classify_material_L4_latin_stone_ivory_text_fallback` | "lapis"/"saxum" → stone; "eburneus" → non-stone; residual "other" text stays unmatched. |
| L5 | `test_classify_material_L5_tin_word_boundary_not_substring` | "tin" alone still matches; "tiburtinus"/"triestina" alone no longer falsely match; the real corpus shape "lapis, tiburtinus?" resolves STONE via L4, not non-stone via the old bug. |
| L6 | `test_14_codex_r2_refutes_on_stayed_majority_not_unanimity_L6`, `test_14_codex_r2_indeterminate_mixed_on_exact_tie_L6` | A synthetic 1-shifted/3-stayed-of-4 scenario: OLD code (unanimity) would land INDETERMINATE(mixed); NEW code (majority) correctly lands REFUTES — the precise divergence band the real alpha data (13/14 shifted) never reaches. Plus the exact-tie (2/4) → INDETERMINATE(mixed) branch. |
| L7 | `test_as_int_L7_robust_numeric_coercion` | Direct boundary coverage: plain/decimal/native-float/quoted-string/quoted-decimal/negative/empty-quoted/non-numeric, all against the real production function. |
| L13 | `test_09_18_validation_gate_parks_no_computation_L13`, `test_config_validation_gate_parked_ids_are_still_registered_live_rivals` | An EMPTY `FakeDb` (raises on any query) proves zero DB access for all 3 parked tests; verdict string, empty `computed_quantities`, carried `d_dep`, and notes citing both the gates doc and the ledger are all checked exactly; confirms the 3 remain full `LIVE_RIVAL_IDS`/`THRESHOLDS`/`MIN_DATA_RULES` members, distinct from `MECHANICAL_PARKS`/`REGION_INVENTORY_PARKS`. |

Also touched, unaffected (confirmed still passing, no update needed):
`test_config_family_registered_sizes_include_parked_members`,
`test_family_context_reports_multiplicity_gap_disclosure`,
`test_parked_rival_reports_cover_all_five` (extended with one additional assertion
that the 3 L13 parks are absent from this reporting path — the two park categories
stay distinct) — none of these read `VALIDATION_GATE_PARKED_TEST_IDS` or any
ledger-touched function.

## 7. Execution status — THE single official run

```
$ python3 ops/popper/edr_rivals/run_all.py --unblind
--unblind passed. Executing all 9 live rivals + Pattern-3. This is a ONE-TIME,
registered-verdict execution per the gate-5 freeze report.

Wrote ops/popper/edr_rivals/results/edr_gate5_execution_20260810T191056Z.json
Wrote ops/popper/edr_rivals/results/edr_gate5_execution_20260810T191056Z.md
EXIT CODE: 0
```

No crash — the A7 void-run rule was not invoked. Full captured output (stdout +
stderr) preserved verbatim at `docs/generated/quincunx_cir_rerun_log_20260810.txt`.
Preflight: all 3 fingerprints PASS (EDH/EDB/EDR-eligible-manifest, byte-identical to
alpha). Overlap audit headline: byte-identical to alpha (746 eligible / 0.709%).

## 8. Post-repair hashes — see §1 (single combined table)

## 9. Delta summary

Full pre-registered delta: `docs/generated/quincunx_cir_delta_20260810.md`. Headline:

- **6 of 9 live-rival slots computed this run**; **5 of those 6 are byte-identical to
  alpha** in verdict and every intermediate quantity (02-codex-r2, 02-grok-r2,
  03-grok-r2, 06-codex-r3, 14-codex-r2) — a direct, load-bearing confirmation that
  L1/L3/L4/L5/L7/L8/L10 are empirically inert on every live path this run, exactly as
  the ledger predicted.
- **1 genuine verdict flip**: 18-codex-r2, FAVORS(PARTIALLY) → REFUTES(within scope),
  driven entirely by L9. EDR bracket density moves from ~1–2.5% (alpha, every
  province trivially below the 35% threshold) to ~49–78% (corrected, every province
  now above it) — a ~48–76pp per-province swing. The corrected REFUTES additionally
  picks up a CONTAMINATION-SENSITIVE robustness caveat (alpha's FAVORS was robust to
  the R=1,483 adversarial sweep; corrected REFUTES is not).
- **3 of 9 live-rival slots PARK under L13** (09-grok-r1, 09-grok-r2, 18-grok-r2) —
  zero DB access, zero computed quantities; their alpha verdicts (FAVORS, REFUTES,
  FAVORS respectively) stand as the published record; the corrected-instrument delta
  for these three is deferred to a future frame with repaired evidence.
- **Pattern-3 (Rome triangulation)** and **all 5 pre-registered parks** (09-codex-r3,
  18-grok-r1, 11-grok-r1, 12-codex-r1, 12-grok-r1): byte-identical to alpha.
- Preflight and the Sec6.7 overlap audit: byte-identical to alpha (no eligibility
  movement anywhere in this replication).

Per protocol condition 19: the original registered run retains priority as the blind
test, permanently; this corrected run addresses measurement validity, not independent
confirmation.

---

## COUNTERSIGNATURE — 10 August 2026 (the replication adjudicated; the CIR is complete)

**The re-execution and its delta are ACCEPTED as reported.** Verified at signature:
the L9 gold-sample agreement (750/750, zero opposite-direction failures corpus-wide),
the 162/162 test state including the four transport tests correctly updated to the
ledger's semantics, exit-0 single execution, and the delta's flip mechanics.

**The result, plainly:** one genuine verdict flip in six computed analyses —
**18-codex-r2, FAVORS → REFUTES (within scope)** — produced entirely by the ledger's
one substantive live repair: restoring the Leiden serialization that the alpha
extraction had stripped from EDR's edition text. With the field repaired, EDR's
bracket density lands at 49–78% across all fourteen provinces, inside EDH's own
range, and the rival's condition is satisfied nowhere. The alpha FAVORS is thereby
established as a pure serialization artifact — measured, mechanised, repaired under a
spec-determined rule, and reversed, with the gold-label-validated detector reading
the repaired field. The new CONTAMINATION-SENSITIVE label on the corrected verdict
publishes with it. The other five computed analyses are byte-identical to alpha —
the strongest possible demonstration that the remaining repairs, including the
largest instrument correction in the audit (10,791 records), were empirically inert
on every live path: a ten-percent instrument error with zero verdict movement, shown
as distance-to-threshold, exactly as the registered delta format intended.

**One defect disclosed under condition 18, not repaired in this run:** the closed
ledger's L1 prose said nine ids but its binding list carried eight — the
countersignatory's own transcription slip dropped Marmor-taenarischer (lod 747, one
record, A0-proven by Lane A, zero live-path effect). It belongs to the next version's
ledger, per the protocol's own rule that post-execution discoveries are disclosed,
never patched into the completed run.

**Standing outcomes:** the three gate-parked analyses emit PARKED-BY-VALIDATION-GATE;
their alpha verdicts stand as the published record with their existing post-result
assessments; their corrected deltas are deferred to future frames with repaired
evidence. Pattern-3 and the five pre-registered parks are unchanged. Thresholds,
d_dep values, populations, and the manifest were untouched throughout, as the ledger
required.

The Corrected-Instrument Replication is COMPLETE. Publication of the corrected
results and the full delta follows under the both-publish commitment already in
print. Countersigned: Claude (orchestrator).
