Skip to content

fix(dead_candidate_scan): stop one unbalanced backtick in docs/index.md from hiding citations - #4745

Merged
phasetr merged 4 commits into
mainfrom
fix/dead-candidate-scan-md-backtick-parity
Jul 27, 2026
Merged

fix(dead_candidate_scan): stop one unbalanced backtick in docs/index.md from hiding citations#4745
phasetr merged 4 commits into
mainfrom
fix/dead-candidate-scan-md-backtick-parity

Conversation

@phasetr

@phasetr phasetr commented Jul 26, 2026

Copy link
Copy Markdown
Owner

What was wrong

Markdown code spans are paired positionally, so one unbalanced backtick does not
merely lose its own span: it inverts the parity of the rest of its line. Prose is
then read as a citation and the citations are read as prose, and nothing warns.

Measured on main 075bcd39:

  • docs/index.md:1809 is a single 52186-character progress row that spells
    `ContinuousOn`.continuousAt` with three backticks where two were meant
    (they sit at columns 9925/9938/9952). From column 9952 on the parity is
    inverted: the line contributes 218 tokens and none of them names
    magnetizationAlongExhaustion, though the raw line spells it six times.
  • Consequence at verdict level: magnetizationAlongExhaustion_differentiable_beta_gen
    came out safe-to-delete with the reason "no citation in the scanned
    documentation", while docs/index.md:1809 cites it as
    `magnetizationAlongExhaustion_continuous_beta_gen` + `_differentiable_beta_gen` + ...
  • docs/index.md:1200/:1201 carry the sibling shape, a code span opened on one
    line and closed on the next, which `[^`\n]+` cannot match under any
    pairing; it hid latticeBallBoundaryEdges_card_le_sphere and
    latticeSphere_card_le' as tokens.
  • Those three are the only lines with an unpairable backtick in README.md,
    docs/**/*.md and tex/proof-guide.tex as of 075bcd39. README.md:66/:69
    are fence delimiters and pair off.

A second, independent hole sat behind the first: _differentiable_beta_gen
matches three declarations (correlation / magnetization / susceptibility), so
even with the tokens restored the family-label rule attributed the citation to
nobody and the verdict stayed safe-to-delete.

A third hole, one level up, was found by review of this branch and is fixed in
17074732: the fenced alternative of _MD_TOKEN_RE is re.DOTALL and
unbounded, so an unbalanced run of three or more backticks pairs with the next
run anywhere in the file and its body swallows every line in between — and
crediting the whole match then let the very match that hides those citations
certify their backticks as read. Measured counterexample:

text = "start ```\ncite `_alpha_gen` here\nand `beta{,_two}_gen`\nend ```\n"
unpaired_backticks(text) == {}          # clean bill of health
_markdown_source(...).tokens == []      # both citations gone

Both shapes are the fragment ones (a brace alternation, an elided suffix) with
no verbatim fallback: the fenced body is split on whitespace and _nameish
rejects the pieces that still carry a backtick.

What changed

scripts/dead_candidate_scan.py plus its tests and fixtures header; no Lean, no
docs/, no tex/, no CI wiring.

  • unpaired_backticks(text) returns every backtick no code span absorbed, keyed
    by line. A well-formed span carries no backtick inside it, so it exonerates
    nothing but its own delimiters. A fenced match is credited with its two
    delimiters and never with its body, so the text above now reports
    {2: 2, 3: 2} and both citations are recovered. A fenced block that
    legitimately quotes a backtick is charged too: the price is one coverage
    warning plus one superset re-read, both keep-only.
  • unbalanced_fence_run(text) reports an odd number of fence runs. It catches
    the one shape the backtick count cannot: a six-backtick run reads as a fenced
    match with an empty body, so every backtick pairs off while the file is still
    malformed. Charge-only, like the rest of the coverage warnings — it reaches
    neither a verdict nor the exit code.
  • _markdown_source does not skip a flagged line — skipping drops exactly
    the citations with no verbatim fallback. It re-reads the line with
    pairing_independent_tokens, which splits on backticks: every code-span body
    is a maximal backtick-free substring of its line, so the split is a superset
    of the bodies any pairing can produce. The re-read can add citations and
    cannot drop one. The defect is still reported, in a coverage warnings, Markdown backtick parity block, so the Markdown gets repaired rather than
    worked around.
  • elided_prefix_matches treats a suffix citation whose elided prefix is spelled
    out on the same line as a shorthand for the siblings that share that prefix,
    rather than a family label attributed to nobody. A family label on a line that
    elides nothing still rescues nobody.
  • LIMITATIONS gains L10 (backtick parity, fences included) and L11 (family
    labels); the module docstring gains the corresponding fifth design rule.

Corrections made after review (389b91f517074732)

Two numeric claims shipped in 389b91f5 were false as written and are replaced
by the measurement:

  • elided_prefix_matches and L11 said charging every family-label match
    "leaves no reachable safe-to-delete verdict at all". Measured by applying
    that mutant to the real file and sweeping all 10965 declarations: it leaves
    232 (published 6731 / uncertain 1467 / load-bearing 2535 / safe 232), a
    collapse from 1458 (84%), not an elimination. The rejection stands on the 84%
    collapse plus --expect FAIL (1 of 18), and the source now says that. The
    fragment census quoted alongside it (514 of 742 … 5253 of 11000) did not
    reproduce either and is restated as measured: 531 of the 759 distinct
    fragment-shaped tokens that resolve at all match two declarations or more,
    union 5895 of 11000.
  • The same docstring said a genuine family label (_ferromagnetic, _pos)
    "elides no prefix anyone spelled out and is unaffected". Measured on the real
    documentation, _ferromagnetic is charged 155 times and _pos 9
    times. Counterfactual on the 223 _ferromagnetic candidates: rule off leaves
    92 safe-to-delete, rule off for _ferromagnetic only leaves 60,
    rule on leaves 47 — so that one label accounts for 13 of the 45 that move
    and other elided fragments on the same rows for the remaining 32. The
    docstring now states this instead of denying it. (The 932 declarations
    figure is likewise replaced: the rule makes 2206 charges over 1077
    distinct declarations.)

The 84%/232 correction does not change the decision, and neither correction
changes a line of classifier behaviour.

Measurements

Run on this branch against main 075bcd39.

  • docs/index.md coverage warnings: 3 (:1200, :1201, :1809); the run
    reports 0 recovered tokens on :1200, 2 on :1201, 277 on :1809.

  • The 10 declarations of IsingModel/AmbientLattice/BetaDerivativeMagnetization.lean:
    before, 3 published / 6 load-bearing / 1 safe-to-delete; after, 5 published /
    4 load-bearing / 1 uncertain / 0 safe-to-delete. The one that moved is
    magnetizationAlongExhaustion_differentiable_beta_gen, now charged by
    shorthand docs/index.md:1809: `_differentiable_beta_gen` .

  • Whole-library sweep, all 11000 non-anonymous declarations classified (10965
    distinct names) on main and on 17074732: 235 verdicts move, and every move is
    toward keeping —

    move count
    safe-to-delete -> uncertain 142
    safe-to-delete -> load-bearing 53
    safe-to-delete -> published-result 20
    load-bearing -> published-result 9
    uncertain -> load-bearing 9
    uncertain -> published-result 2

    No move has safe-to-delete as its target, and the after-safe set is a strict
    subset of the before-safe set. Totals: safe 1673 -> 1458, uncertain 561 -> 692,
    load-bearing 2031 -> 2084, published 6700 -> 6731. Documentation family labels
    844 -> 512.

  • The fence repair is not reachable on the current corpus and changes nothing
    there: no fenced body in README.md or docs/**/*.md contains a backtick, and
    every scanned Markdown file has an even number of fence runs. The same
    10965-declaration sweep on 389b91f5 and on 17074732 is identical —
    0 verdicts moved, safe stays 1458, family labels stay 512. README.md:66
    and :69 already carry fences, so one added or removed delimiter reproduces
    the counterexample.

  • _ferromagnetic family calibration (--pattern '_ferromagnetic$', 223
    candidates): 92/44/52/35 -> 47/77/64/35 safe/uncertain/load-bearing/published;
    45 candidates leave safe-to-delete, 33 to uncertain and 12 to
    load-bearing through the delete-closure. Zero-consumer count is unchanged at
    112. FamilyCalibrationTest and the fixtures header are refreshed together;
    the header had also drifted, still carrying 263/132/143 from main 0922a812.

Tests

  • 15 new cases: MarkdownBacktickParityTest (12) and ElidedFragmentTest (3).
    They cover the synthetic parity flip, the swallowed fenced block and its
    recovered tokens, the run-parity check and the six-backtick shape it alone
    catches, the real docs/index.md warnings and recovered tokens, the end-to-end
    verdict of the declaration the defect offered up for deletion, and both sides
    of the elision rule. test_balanced_lines_and_fences_raise_nothing asserted
    the old silence on a fence whose body quotes a backtick and is rewritten to the
    fail-closed expectation.

  • python3 scripts/dead_candidate_scan.py --self-test: 94 tests, pass (90 on
    389b91f5, 79 on main).

  • python3 scripts/dead_candidate_scan.py --expect scripts/audit/dead_candidate_fixtures.tsv:
    18 rows, pass.

  • python3 scripts/test_audit_gate.py: 166 tests, pass (1 skipped).
    python3 scripts/test_citation_audit.py: 150 tests, pass. Neither module
    imports dead_candidate_scan; the only coupling is test_audit_gate.py's
    import-contract check, and the from audit_gate import (...) list is unchanged.

  • Weakening mutants, each applied alone and reverted, guard tests re-run:

    mutant result
    M1 drop the pairing-independent recovery RED, 4 failures
    M2 make unpaired_backticks return {} RED, 8 failures + 1 error
    M3 restore "a 2+-match fragment is attributed to nobody" RED, 3 failures
    M4 charge every match of every family label RED, 5 failures, fixtures FAIL 1 of 18
    M5 credit a fenced match with its whole span RED, 2 failures

    M5 is new with this round and turns red exactly the two tests that pin the
    fence repair: test_a_backtick_inside_a_fence_is_charged_not_exonerated and
    test_the_swallowed_citations_are_recovered_and_reported.

  • lake build was not run: no Lean source is touched by this branch.

Scope notes

  • The unbalanced backtick in docs/index.md:1809 is still there. Repairing the
    Markdown is editorial and stays out of this branch, which only stops the
    scanner from reading the damaged line as evidence of absence.
  • Two shapes stay outside the superset step and are recorded as L10: a code
    span opened on one line and closed on the next is unmatchable in either
    pairing (both lines still raise the warning), and prose read as a citation by
    the re-read can only add uncertain, never remove it.
  • Three review findings are deliberately left as recorded rather than changed
    here. elided_prefix_matches uses a lexical startswith with no identifier
    boundary (58 charges over 47 sites today, and an identifier-boundary variant
    changes 0 of 10965 verdicts); the elision rule's locality bound is the
    documentation line, which on a 52186-character progress row is wide; and
    test_the_real_index_raises_its_three_warnings pins line numbers that an
    editorial Markdown repair would move. All three are over-keep or editorial, so
    none can produce a false safe-to-delete.
  • Follow-up on this branch: the retracted figures had survived in the
    ElidedFragmentTest docstring of scripts/test_dead_candidate_scan.py
    ("5253 of 11000 declarations, no reachable safe-to-delete"). It now states
    the measured 5895 of 11000 and the collapse from 1458 verdicts to 232,
    matching dead_candidate_scan.py; grep -rn "5253\|no reachable" over the
    worktree finds no other copy.

🤖 Generated with Claude Code

phasetr and others added 4 commits July 27, 2026 06:50
…ty fix

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Markdown code spans are paired positionally, so one unbalanced backtick does
not merely lose its own span: it inverts the parity of the rest of its line,
and the tokenizer reads prose as citations and citations as prose with nothing
to warn about.

docs/index.md:1809 (a single 52186-character progress row) spells
`ContinuousOn`.continuousAt` with three backticks where two were meant. From
that column on, the line's 218 tokens named none of the six occurrences of
`magnetizationAlongExhaustion` the raw text carries, and
`magnetizationAlongExhaustion_differentiable_beta_gen` came out
`safe-to-delete` although the same line cites it. Lines 1200/1201 carry the
sibling shape (a span opened on one line and closed on the next), which
`[^`\n]+` cannot match in any pairing.

Two changes, both additive on the citation side:

* `unpaired_backticks` reports every backtick no code span absorbed, and
  `_markdown_source` re-reads those lines with `pairing_independent_tokens`
  instead of skipping them. Splitting a line on backticks yields a superset of
  the bodies any pairing can produce, so the re-read can add citations and
  never drop one; the defect is still printed, as a Markdown coverage warning,
  so the documentation gets repaired rather than worked around.
* `elided_prefix_matches` treats a suffix citation whose elided prefix is
  spelled out on the same line (`` `foo_continuous_gen` + `_differentiable_gen` ``,
  the notation both documentation files use) as a shorthand for the siblings
  that share that prefix, instead of a family label attributed to nobody.
  Charging every match of every family label was measured first and rejected:
  it touches 5253 of the library's 11000 declarations and leaves no reachable
  `safe-to-delete` verdict, including the fixtures' isolated-declaration row.

Measured on this branch against main 075bcd3, whole library, 11000
declarations: 235 verdicts move, and every one of them moves toward keeping
(142 safe -> uncertain, 53 safe -> load-bearing, 20 safe -> published, 9
load-bearing -> published, 9 uncertain -> load-bearing, 2 uncertain ->
published). None moves toward `safe-to-delete`. The `_ferromagnetic` family
calibration goes 223 -> 47 safe / 77 uncertain / 64 load-bearing / 35
published (was 92 / 44 / 52 / 35); the fixtures header carried older numbers
still and is refreshed with it.

Tests: 11 new cases in `MarkdownBacktickParityTest` and `ElidedFragmentTest`;
`python3 scripts/dead_candidate_scan.py --self-test` runs 90 and passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… it swallows

Review of this branch found one fail-open and two false numeric claims. All
three are repaired here; no verdict on the real tree changes.

1. `unpaired_backticks` credited a fenced match with its whole span. The fenced
   alternative of `_MD_TOKEN_RE` is `re.DOTALL` and unbounded, so an unbalanced
   run of three or more backticks pairs with the next run anywhere in the file
   and its body swallows every line in between -- and the same match then
   certified the swallowed citations' backticks as read. Measured on

       "start ```\ncite `_alpha_gen` here\nand `beta{,_two}_gen`\nend ```\n"

   the old reading returned `{}` and both citations were lost (the fenced body
   is split on whitespace and `_nameish` rejects pieces that still carry a
   backtick; neither shape has a verbatim fallback). A fenced match is now
   credited with its two delimiters only, so that text reports `{2: 2, 3: 2}`
   and `_markdown_source` recovers both tokens. An odd number of fence runs is
   reported on top (`unbalanced_fence_run`), which catches the one shape the
   backtick count cannot: a six-backtick run reads as a fence with an empty body.
   Both are charge-only and reach neither a verdict nor the exit code.

   Not reachable on the current corpus: no fenced body in README.md or
   docs/**/*.md contains a backtick, and every file has an even number of fence
   runs, so the full 10965-declaration sweep is byte-identical before and after
   (safe-to-delete 1458, family labels 512, zero verdicts moved). Against main
   075bcd3 the branch still moves 235 verdicts, all keep-direction, with zero
   moves into safe-to-delete and safe 1673 -> 1458 a strict subset.

   `test_balanced_lines_and_fences_raise_nothing` asserted the old silence on a
   fence whose body quotes a backtick; it is rewritten to the fail-closed
   expectation and joined by three cases pinning the recovery and the run-parity
   check.

2. `elided_prefix_matches` and LIMITATIONS L11 said charging every family-label
   match "leaves no reachable safe-to-delete verdict at all". Measured: that
   option leaves 232 (published 6731 / uncertain 1467 / load-bearing 2535), a
   collapse from 1458 rather than an elimination. The rejection stands on the
   84% collapse plus `--expect` FAIL (1 of 18); the docstring now says so, and
   the fragment census it quotes is restated as measured (531 of 759 distinct
   resolving fragments match 2+ declarations, union 5895 of 11000).

3. The same docstring said a genuine family label "elides no prefix anyone
   spelled out and is unaffected". Measured: `_ferromagnetic` is charged 155
   times and `_pos` 9 times. Switching the rule off on the 223 `_ferromagnetic`
   candidates leaves 92 safe-to-delete and on leaves 47; charging that one label
   accounts for 13 of the 45 that move and other elided fragments for the other
   32. What rescues nobody is a family label on a line that elides nothing.

Tests: `--self-test` 94 pass (90 before), `--expect` 18 rows pass,
test_audit_gate.py 166 pass (1 skip), test_citation_audit.py 150 pass. Mutants,
each applied alone and reverted: M1 drop the recovery RED (4 failures), M2 blind
`unpaired_backticks` RED (8 failures + 1 error), M3 restore "2+-match = nobody"
RED (3), M4 charge every family-label match RED (5 + fixtures FAIL 1 of 18), and
the new M5 credit a fenced match with its whole span RED (2). No Lean, docs/ or
tex/ file is touched, so `lake build` was not run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… with the corrected measurement

The retracted figures survived in the test module after the scanner module was
corrected. `ElidedFragmentTest` still described charging every match of every
family label as touching "5253 of 11000 declarations, no reachable
safe-to-delete"; the measured sweep touches 5895 of 11000 and leaves 232
`safe-to-delete` verdicts (down from 1458). The docstring now states the same
figures as `dead_candidate_scan.py` (module docstring and rule L11).

Docstring only; no test, fixture or classifier behaviour changes.

Measured on this commit:
- `python3 scripts/dead_candidate_scan.py --self-test`: 94 tests, OK
- `python3 scripts/test_audit_gate.py`: 166 tests, OK (skipped=1)
- `python3 scripts/test_citation_audit.py`: 150 tests, OK
- `grep -rn "5253\|no reachable"` over the worktree: no matches

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@phasetr
phasetr marked this pull request as ready for review July 27, 2026 00:50
@phasetr
phasetr merged commit 2aa350e into main Jul 27, 2026
1 check passed
@phasetr
phasetr deleted the fix/dead-candidate-scan-md-backtick-parity branch July 27, 2026 00:50
phasetr added a commit that referenced this pull request Jul 28, 2026
…/Mayer and J = 0 pseudo-mass wrapper modules (safe-to-delete batch 4) (#4754)

Part of #4746 (Item A, batch 4).

Deletes the nine fully-dead modules of batch 4 — **31 declarations, 712 lines** —
and retracts every documentation claim that counted them. The batch is the union
of the three cascade-0 groups of the 32 lane-eligible fully-dead modules, so no
declaration anywhere else in the library loses its last consumer.

Measured on `fd7a2a71` (`git ls-remote origin main` at branch time) in a
throw-away worktree with an APFS-cloned warm `.lake`.

## What was deleted

| module | decls | lines |
|---|---|---|
| `Concrete/LatticeGraphCorrelation/PartitionFreeEnergyBoundsAlongExBridges.lean` | 4 | 78 |
| `Concrete/LatticeGraphCorrelation/PartitionFreeEnergyMonotonicityFreeEnergyAlongEx.lean` | 3 | 56 |
| `Concrete/LatticeGraphCorrelation/PartitionFreeEnergyRegularity.lean` | 4 | 87 |
| `Concrete/LatticeGraphCorrelation/PartitionFunctionGeneralAnalyticity.lean` | 5 | 83 |
| `Concrete/LatticeGraphCorrelation/PartitionFunctionSymmetryCubicAbsH.lean` | 2 | 42 |
| `Concrete/LatticeGraphCorrelation/MagnetizationCorrelationBasic.lean` | 4 | 72 |
| `Concrete/LatticeGraphCorrelation/MayerFilterConnected.lean` | 4 | 96 |
| `Concrete/LatticeGraphCorrelation/MayerVdBounds.lean` | 3 | 81 |
| `PseudoMass/FromParamsHZero/JZeroJointRegularity.lean` | 2 | 117 |
| **total** | **31** | **712** |

The line total is 712, not the 721 the batch-4 design recorded: the design's
per-file figures are each one higher than `wc -l`, and `git diff --stat` charges
712 deletions.

Build jobs go 5015 → 5006. **No build-time claim is made.** Own elaboration cost
of these modules is at or below the per-module import-only baseline (issue #4724),
so the wall-clock effect is not measurable and none is asserted.

## Declaration-level double confirmation

The 31 declarations were re-extracted independently of the design, with a
line-folding-aware extractor (a same-line `^theorem NAME` regex silently misses
every wrapped declaration, and did so on a first pass here). All 31 names **and
their line numbers** reproduce the design's appendix exactly.

A boundary-aware reference check over **every tracked file** (not only
`IsingModel/`, `docs/`, `tex/`) found **0** genuine references. The four raw
`git grep` hits are all prefix collisions with surviving siblings, and none is a
reference to a deleted declaration:

| raw hit | why it is not a reference |
|---|---|
| `PartitionFreeEnergyBoundsFeAlongExId.lean:46` | `…_nonneg_of_ferromagnetic` — different identifier |
| `PartitionFunctionSymmetryLog.lean:63,64`, `PartitionFunctionSymmetryLogCubic.lean:29,39` | `log_…` prefix — different identifiers |
| `PolymerFreeEnergyEpsilonSharpening.lean:31` | `…_minus_one_pow_at_zero` — different identifier |

## Requirement (i): `--lean` cross-check

`python3 scripts/dead_candidate_scan.py <31 names> --lean`, run against the green
pre-deletion build (scanner blob `7f47c6bd`, unmodified), exit 0:

```
--lean cross-check: 31 candidate(s) compared against the elaborated graph;
no consumer seen by Lean was missed on a safe-to-delete verdict
canary: 1021 declarations carrying 'Λ'x1018, 'β'x1, 'σ'x2 each find themselves: PASS
canary: 10162 code citations in tex/proof-guide.tex, none broken across a line: PASS

-- published-result: 0 --   -- load-bearing: 0 --
-- uncertain: 0 --          -- safe-to-delete: 31 --
-- coverage warnings: 0 --
```

Every one of the 31 carries the reason *"no reference outside the delete set, no
citation in the scanned documentation"*.

## Requirement (v): the two sweeps

Both sweeps run the full-library scan (`--pattern '.'`, 10,868 scanned
declarations) on the **pre-deletion library**, changing only the documentation.

| sweep | docs/tex | safe-to-delete | delete set |
|---|---|---|---|
| 1 | pre-deletion | 1127 | 31/31 safe |
| 2 | this commit's | 1123 | 31/31 safe |

**Condition (v) assertion: 0 violations.** No declaration outside the delete set
moves toward `safe-to-delete`. All seven verdict moves are in the protective
direction, and they are quoted rather than summarised:

```
safe-to-delete -> published-result  freeEnergyAlongExhaustion_latticeGraph_cubicExhaustion_monotone_J
safe-to-delete -> published-result  freeEnergyAlongExhaustion_latticeGraph_cubicExhaustion_monotone_beta
safe-to-delete -> published-result  freeEnergyAlongExhaustion_latticeGraph_cubicExhaustion_monotone_h
uncertain      -> published-result  freeEnergyAlongExhaustion_latticeGraph_cubicExhaustion_upper_bound
uncertain      -> published-result  freeEnergyAlongExhaustion_latticeGraph_upper_bound
uncertain      -> published-result  mayerPartialSum_Λ_latticeGraph_analyticOnNhd
safe-to-delete -> uncertain         mayerPartialSum_Λ_latticeGraph_differentiable
```

The first three are the point of the surgical brace split: replacing the nested
brace by two exact labels turns a citation the scanner could not resolve into
citations it can. The 29 declarations the design predicted could be stranded by a
careless line rewrite are all still `published-result`.

An **independent fail-closed re-expansion** of every backticked token in
`docs/index.md` and every `\texttt{}`/`\path{}` token in `tex/proof-guide.tex`
(recursive brace expansion, `*` as zero-or-more, cross-checked against a
folding-aware index of all 10,963 library declarations) confirms the design's
result and adds nothing: of every family token that covers at least one deleted
name, **exactly one loses all of its members** —
`` `BddAbove_freeEnergyAlongExhaustion_latticeGraph*` `` at `docs/index.md`, which
this PR drops. Every other covering token keeps survivors (e.g.
`` `correlation_*_latticeGraph` `` 19 members → 17, `` `freeEnergyAlongExhaustion_latticeGraph_*` `` 103 → 98).

## Documentation retraction

Prose *counts* are invisible to the scanner, so each affected wrapper family was
measured layer by layer instead. **Two of the four count retractions below were
not in the batch-4 design** and were found by that measurement.

| site | before | after | in design? |
|---|---|---|---|
| `docs/index.md` PR #1595 / `tex` §18.5 | `12 new thin wrappers (3 abstracts × 4 layers)` | `12 … ; the three ℤ^d Λ-layer wrappers had no consumers and were deleted in PR #4754, leaving 9 across three layers` | yes |
| `docs/index.md` PR #1610 / `tex` §18.6 | `20 thin wrappers (5 abstracts × 4 layers)` | `20 … leaving 15 across three layers` | yes |
| `docs/index.md` PR #1611 / `tex` §18.6 | `24 thin wrappers (8 abstracts × 3 missing layers)` | `24 … leaving 20 across the remaining layers` | **no** |
| `docs/index.md` PR #1613 / `tex` §18.5 | `20 thin wrappers (5 abstracts × 4 layers)` | `20 … leaving 16` | **no** |

PR #1613 loses 4 and not 5 because the ℤ^d Λ-layer
`mayerPartialSum_Λ_latticeGraph_analyticOnNhd` lives in another module and
survives; the retraction says so.

Family labels and module lists:

| site | before | after |
|---|---|---|
| `docs/index.md` module-directory bullets | 4 bullets naming the deleted concrete modules | removed (8 lines) |
| `docs/index.md` §4.6 monotonicity row | `` `freeEnergyAlongExhaustion_latticeGraph{,_cubicExhaustion}_{upper_bound,monotone_{J,h,beta}}` `` (8 members, 3 deleted) | `` `…latticeGraph{,_cubicExhaustion}_upper_bound` `` + `` `…latticeGraph_cubicExhaustion_monotone_{J,h,beta}` `` |
| `docs/index.md` §4.6 bounds row | `` , and `BddAbove_freeEnergyAlongExhaustion_latticeGraph*` now live in `PartitionFreeEnergyBounds.lean` `` (2 members, both deleted) | token dropped; location claim widened to `` `PartitionFreeEnergyBounds.lean` and its child modules `` |
| `tex` ×4 "now live in …" sentences | claimed the deleted ℤ^d Λ-layer children among the surviving ones | each names the surviving children and records the ℤ^d Λ-layer deletion |

`docs/index.md:1411` (`` `correlation_*_latticeGraph` ``, 17 of 19 survive) and
`docs/index.md:1983` are deliberately left alone, as the design determined.

The retraction sentences describe the removed wrappers **by layer, never by
name**. A first draft named them, and sweep 2 caught it immediately: 11 of the 31
flipped to `published-result` because the retraction itself had become a citation
of a declaration about to stop existing.

**12 in-library module doc comments** that pointed at the deleted modules were
corrected in the same commit: four parent-module "Moved:"/"Removed:" sections
(`Magnetization.lean:66`, `PartitionFreeEnergyBounds.lean:102`,
`PartitionFreeEnergyMonotonicity.lean:46` and `PartitionFunctionSymmetry.lean:75`)
and eight "extracted from `X.lean`" provenance notes. Five of those eight were the basename trap: a
same-named module survives under `AmbientLattice/SpecialCases/`, so the reference
would silently re-resolve to the wrong file instead of dangling. This closure was
not enumerated by the design.

## Ratchet and floor recalibration

`scripts/test_dead_candidate_scan.py`

* `test_ferromagnetic_family_counts`: `SAFE 47 → 46`, `UNCERTAIN 77 → 78`;
  `LOAD_BEARING` (64), `PUBLISHED` (35), the 223 total and
  `test_zero_consumer_count` (112) are unchanged. Exactly one declaration moves,
  and it moves *away* from `safe-to-delete`:
  `freeEnergyAlongExhaustion_latticeGraph_nonneg_of_ferromagnetic`
  `safe-to-delete → uncertain`, because the deleted sibling was the other member
  of the `nonneg*` component of its family label. The docstring records the
  mechanism.
* Three `docs/index.md` line pins (`1200/1201/1809 → 1192/1193/1801`) shift by the
  8 removed bullet lines. Same three rows, different numbers.

`scripts/test_audit_gate.py` — **the floors are not lowered.**

| counter | `fd7a2a71` | this PR | floor | slack |
|---|---|---|---|---|
| `iter_checked_files()` | 1993 | 1984 | `CHECKED_FILE_FLOOR` 1957 | 27 |
| `iter_lib_files()` | 1986 | 1977 | `LIB_FILE_FLOOR` 1950 | 27 |
| `iter_v4_files()` | 2023 | 2014 | `V4_FILE_FLOOR` 1977 | 37 |

The comment block claimed *"Measured 2026-07-27 … `iter_checked_files()` = 2003
and `iter_lib_files()` = 1996 … The floors sit 46 below the measured counts"*.
Both halves were wrong: 2003/1996 were the counts from *before* PR #4751 deleted
its ten modules, so on `fd7a2a71` the real gap was 36, not 46. The block is
rewritten against the fresh measurement, and it now records the standing
decision that **the next batch that would actually trip a floor must land F1
(per-directory assertions) rather than another recalibration.**

A follow-up commit fixes one figure the rewritten block had carried over unchanged:
the largest library directory is `Concrete` **851**, not 869. 869 predates PR #4751's
ten-module deletion (859 at `fd7a2a71`). Re-measured on this branch, the eight largest
are `Concrete` 851, `AmbientLattice` 275, `ClusterExpansion` 205, `TransferMatrix` 109,
`Peierls` 105, `AmbientComplexAnalyticity` 76, `Inequalities` 71, `Conditioning` 48 —
so the block's claim survives the correction: losing the smallest of the eight takes
`iter_checked_files()` from 1984 to 1936, below `CHECKED_FILE_FLOOR` 1957.

`scripts/audit/citation_baseline.tsv` — 4 rows hand-moved from `AMBIGUOUS` to
`BASENAME_ONLY` (`MayerFilterConnected.lean`, `MayerVdBounds.lean` ×2,
`PartitionFreeEnergyRegularity.lean`, `PartitionFunctionGeneralAnalyticity.lean`).
Deleting one of two files that share a basename makes the tex citation resolve
uniquely, which is a class change, not erosion: **gating findings stay at 960**.
`--update-baseline` refuses a row that rises and directs the change to a hand
edit so the diff shows it, which is what this is.

## Verification, all re-measured here

| gate | result |
|---|---|
| `lake build` | exit 0, **5006 jobs**, build text grepped: 0 `warning`, 0 `error` (the 2 `info:` `ring` notes are present on `fd7a2a71` too) |
| `scripts/audit_gate.py` | PASS — V1 no `axiom` (1984 files), V2 no sorry/admit/native_decide (1984 files), V3 13 capstones all `{propext, Classical.choice, Quot.sound}`, V4 no Japanese (2014 files) |
| `grep -rn "sorry" IsingModel/` | 2 hits, both prose inside doc comments, same as `fd7a2a71` |
| `lake exe GKSTest` | all tests passed |
| `scripts/citation_audit.py` | PASS — 960 gating findings (unchanged), coverage OK, ratchet **0 cleared / 0 new**, tex 1210 raw / 1217 citations, docs 2636 raw / 2700 citations |
| `dead_candidate_scan.py --self-test` | **100 tests OK** |
| `python3 -m unittest scripts.test_audit_gate` | **166 tests, OK (1 skipped)** |
| `citation_audit.py --self-test` | 150 tests OK |
| Japanese scan | none in `tex/`, `docs/`, `README.md`, `IsingModel/`, `scripts/` |

## Final check on a synthesized tree

A separate throw-away worktree at `fd7a2a71` (**pre-deletion library**) carrying
**this commit's** `docs/index.md` and `tex/proof-guide.tex`, scanned with the
unmodified scanner:

```
-- published-result: 0 --   -- load-bearing: 0 --
-- uncertain: 0 --          -- safe-to-delete: 31 --
-- coverage warnings: 0 --
canary (unicode self-find): PASS   canary (tex line-break): PASS
```

and the full-library sweep on that same tree reports **0 condition-(v)
violations**. So the documentation in this commit still justifies deleting
exactly these 31 declarations and nothing else.

On the merged tree, no exact name among the 31 and none of the five uniquely
named deleted modules occurs anywhere in `IsingModel/`, `docs/` or `tex/`. The
four shared basenames that remain (`MayerVdBounds.lean`, `MayerFilterConnected.lean`,
`PartitionFreeEnergyRegularity.lean`, `PartitionFunctionGeneralAnalyticity.lean`)
all resolve to the surviving `AmbientLattice/SpecialCases/` modules.

## Follow-up recorded, not fixed here

The three scanner fail-open constructs found while validating this batch are
recorded as a child item of #4746 and are deliberately out of scope for this PR:
nested braces, a bare trailing `*` with no separating underscore, and `*` read as
one-or-more. All three are fail-open on the deletion side, the same polarity
class as #4745. The affected sites in this batch were handled by hand and
verified by the independent expander above.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant