Repository navigation
Conversation
The guard checked the 2020–2025 retrospective errors (2.29, 1.65, 0.51 and 0.90 percent) and the 2025 evaluation errors (1.17, 2.58 and 0.75 percent) against the whole document. The body restates 1.17, 2.58 and 0.75, so the guard passed when one of them changed in the abstract alone. The guard now also reads each of these figures from the abstract alone, beside the rule it scores, so a swap between rules fails too. It also ties the abstract's count of four projection rules to the retrospective artifact. The abstract slice now comes from section(): when "abstract: |" or "# Introduction" goes missing or repeats, the slice comes back empty and every abstract tie fails instead of reading the whole paper. New tests change each figure in the abstract alone, swap two pairs, and rerun every case with the original wording restated in the body. Another test renames, repeats and restyles the slice's markers. No paper text or number changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check_paper.py read the whole paper in 39 figure ties: 27 has_number and 3 has_ordered calls that fell back to QMD, and 9 `literal in QMD` tests. Each tie now reads the section that states its figure, through slices that fail closed when a heading goes missing or repeats. stated() keeps each old literal as the whole-paper search matched it, line breaks included, and adds phrases that hold the figure beside what it measures, so the guard still rejects every paper it rejected before. has_number and has_ordered now require their text. The abstract alone states the four 2020-2025 error rates, so its decoy cases now list the "current projection prose" ties as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SECTION_PROSE_MUTATIONS holds 67 cases. Each changes one figure, or swaps two, in the section that states it. test_section_figures_drift_alone expects every listed tie to fail. test_section_pins_reject_a_figure_restated_elsewhere restates the original sentence under the discussion heading and expects exactly the listed ties to fail. test_section_slices_fail_closed_without_unique_markers renames or repeats seven slice headings. GuardSourceTests parses the guard and fails on any has_number or has_ordered call that names no text or reads QMD, and on any `in QMD` test; it finds 39 in the guard at 0fd4223. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The module docstring, the comment on block (d) and stated() now say what the guard does instead of what it once did: which slices keep line breaks, which normalize whitespace, and why a stated() pin fails wherever a whole-paper search for its figure would. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 of 16 tasks
MaxGhenis
force-pushed
the
max/abstract-guard-ties-20261106
branch
from
November 7, 2026 17:34
0fd4223 to
f95d8d7
Compare
5 of 8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR builds on #13, which builds on #12, so merge both first. Once they merge, we'll rebase this branch onto
master.What changes
scripts/check_paper.pysearched the whole paper in 39 of its figure ties: 27has_numberand 3has_orderedcalls that fell back toQMD, and 9literal in QMDtests. When a figure changed in the section that states it, its tie still passed if the same numeral appeared anywhere else in the paper. For example, the 0.7 that opens Appendix A's standard-error range matched the 0.7 exponent in §2.2's formula, and §6.2's 1.17, 2.58 and 0.75 matched the abstract's copies.Each tie now reads the section that states its figure. Six new slices cover §3.2's sample sensitivities, §5.3's five-year benchmark, §6.1's committed estimate, §6.2's evaluation, §6.3's comparator check and Appendix A, and the link ties read #12's Appendix B slice.
section_source()returns a slice's raw text, andsection()collapses its whitespace. If either heading goes missing, repeats or falls out of order, both return an empty slice and record a failure, so every tie in that section fails.Most ties also hold the figure beside the words that say what it measures, as #13 does for the abstract, so a swap between neighbors fails. A phrase also picks out one copy where a section repeats a numeral. For example, in §6.2 we report a mean absolute error of 1.17 percent, then a signed mean of −1.17 percent, so a bare 1.17 would survive a change to the first.
stated(literal, source, *phrases)checks the figure in the slice's raw text and each phrase in its collapsed text, so rewrapping a line can't split a phrase.Where two sections state a figure, each gets a tie:
](https://…), and §6.1's tie usessection_source()in place of twostr.splitcalls that widened the search when a heading went missing.A second new tie, "2025 SE triple in tenure order", holds §6.2's standard errors of $327, $560 and $393 in tenure order, so a swap between two of them fails. The four 2020–2025 retrospective errors appear in prose only in the abstract, so their "current projection prose" ties now read the abstract.
has_number()andhas_ordered()now require the text they search, so no tie can fall back to the whole paper.The guard keeps all 170 of #13's check labels and adds the two new ties. The PR changes no paper text or number.
Tests
tests/test_paper_guard.pyaddsSECTION_PROSE_MUTATIONS, 67 cases that each edit one section through #12'smutate_article, across §3.2, §5.3, §6.1, §6.2, §6.3 and both appendices. Of these, 49 move one figure, 10 swap two figures, 4 point links atmaininstead of the pinned commit, 3 change a spelled-out count and 1 flips a sign. Each of the 62 ties that read a section slice, including #13's abstract ties, fails in at least one case.Three tests run the cases, and a fourth reads the guard's source:
test_section_figures_drift_aloneruns each case and asserts that the guard fails and names the case's ties.test_section_pins_reject_a_figure_restated_elsewherereruns each case after restating the original sentence under the Discussion heading, and asserts that the guard's failures match the case's ties. A tie that read the whole paper would pass these papers.test_section_slices_fail_closed_without_unique_markersrenames five headings and repeats two. Each time, it asserts that the guard reports the heading and fails ties in that section.GuardSourceTests.test_no_tie_reads_the_whole_paperparses the guard and fails if ahas_numberorhas_orderedcall omits its text or passesQMD, or if anintest readsQMD.#13's abstract cases now also expect their "current projection prose" ties to fail. The full suite of 80 tests passes locally in 342 seconds, and CI's drift check runs the guard and the suite on Python 3.12 and passes at the head.
#13's guard (0fd4223) accepts 84 of the 141 mutated papers: 18 of the 67 single-section edits, 63 of the same 67 with a copy of the original sentence under Discussion, and 3 of the 7 heading edits. All four new tests fail against it, and this PR's guard rejects all 141.
Invariants
These hold for every paper the guard reads:
has_number()andhas_ordered()take no default text, andGuardSourceTestsfails on any call orintest that readsQMD.has_number()andhas_ordered()ties searched for contains a digit, and each ordered list starts with one. So a match at token boundaries inside a slice also matches in the full paper and passes the oldintests.stated()checks the figure in the slice's raw text, and its phrases only add conditions.has_ordered()calls that searched the whole paper now search raw slices, so their windows count the same characters.section_source()returns the text the oldstr.splitcalls returned, and otherwise it fails.We also tested invariant 4 with a probe that wrote up to three papers for each occurrence of every literal #13's guard searched the whole paper for. One changes a digit or count word, one splits the occurrence across two lines at its first space, and one deletes it. The probe also renamed and repeated each of the 22 slice headings. Across the 254 distinct papers, both guards reject 176, this guard alone rejects 25, and none passes this guard after failing #13's. Both accept the other 53: in 47, the edit changes or deletes a copy that no tie needs, and 2 repeat a §7 subheading outside §7, where no tie reads. The last 4 change one of Appendix B's two links to
data/PROVENANCE.md.Like the rest of the suite, the tests use only the standard library, and they enumerate every figure the scoped ties pin instead of drawing random edits. The probe needs #13's guard beside this one, so it ran once, outside the suite.
Follow-up
data/PROVENANCE.mdtwice, and its tie passes while either link keeps the pinned commit. Tie the abstract's error rates to the abstract #13's guard has the same gap, and a check that every link into this repository'sblob/tree carries the pinned commit would close it.section_source()names the start heading in its failure message even when the end heading went missing or repeated.APPENDIX_B_TEXTandREPRODUCIBILITYboth slice Appendix B, one with the{.unnumbered}suffix on its headings and one without, and one slice would do.Plan
axiom: n/a: guard and tests only; no policy rule changes
🤖 Generated with Claude Code