Recommended by the first three-edition report (merged in QuantEcon/project-translation#53), where this was the dominant cause of pair-category error at all three editions.
The defect
In scan.py, paired before/after lines route through categorise(), which classifies a pair with no prose on either side as code-or-markup. Unpaired lines take a different path: any added line that is non-blank, and any deleted line that is non-blank, is recorded as an addition/deletion pair — no prose check at all. So a code line, a math block delimiter, or a .translate frontmatter row lands in the omission taxonomy bucket whenever it arrives or leaves without a paired counterpart.
Measured effect
Adjudicated per-class precision from the 2026-08-28 samples (seed 20260828): addition 4/13 at intro, 8/18 at python, 1/20 at programming; deletion 3/4, 2/6, 8/17. The misses are almost entirely code, math and metadata lines. Two commits illustrate the scale: the intro .translate bootstrap (ecd0802f) and the programming frontmatter migration (c1b8dbe) each contribute dozens of metadata rows now counted as omission-taxonomy pairs — at programming they dominate the 255-pair corpus outright.
Recommended fix
Route non-prose unpaired lines to code-or-markup instead of addition/deletion, mirroring what categorise() already does for non-prose paired lines; prose-bearing unpaired lines keep their current categories. This is preferable to dropping non-prose lines entirely because it leaves the total pair count unchanged — the regression test pins the corpus pair count against the 2026-08-04 study, so a category-only shift keeps it green while a drop would not. docs/method.md's category table and the known-limits note should be updated in the same change.
Out of scope here: the terminology↔fluency boundary (measured ~50–70% precision, errors confined to the reader-visible-wording buckets) is inherent to the string rule and already covered by the method's "indicative" caveat, and the occasional mispairing inside rewritten hunks is a separate, smaller artefact.
Recommended by the first three-edition report (merged in QuantEcon/project-translation#53), where this was the dominant cause of pair-category error at all three editions.
The defect
In
scan.py, paired before/after lines route throughcategorise(), which classifies a pair with no prose on either side ascode-or-markup. Unpaired lines take a different path: any added line that is non-blank, and any deleted line that is non-blank, is recorded as anaddition/deletionpair — no prose check at all. So a code line, a math block delimiter, or a.translatefrontmatter row lands in the omission taxonomy bucket whenever it arrives or leaves without a paired counterpart.Measured effect
Adjudicated per-class precision from the 2026-08-28 samples (seed 20260828):
addition4/13 at intro, 8/18 at python, 1/20 at programming;deletion3/4, 2/6, 8/17. The misses are almost entirely code, math and metadata lines. Two commits illustrate the scale: the intro.translatebootstrap (ecd0802f) and the programming frontmatter migration (c1b8dbe) each contribute dozens of metadata rows now counted as omission-taxonomy pairs — at programming they dominate the 255-pair corpus outright.Recommended fix
Route non-prose unpaired lines to
code-or-markupinstead ofaddition/deletion, mirroring whatcategorise()already does for non-prose paired lines; prose-bearing unpaired lines keep their current categories. This is preferable to dropping non-prose lines entirely because it leaves the total pair count unchanged — the regression test pins the corpus pair count against the 2026-08-04 study, so a category-only shift keeps it green while a drop would not.docs/method.md's category table and the known-limits note should be updated in the same change.Out of scope here: the terminology↔fluency boundary (measured ~50–70% precision, errors confined to the reader-visible-wording buckets) is inherent to the string rule and already covered by the method's "indicative" caveat, and the occasional mispairing inside rewritten hunks is a separate, smaller artefact.