Skip to content

Recorded diff shapes become self-checking, and a false cost claim comes out - #502

Merged
derek73 merged 23 commits into
masterfrom
claude/differential-stale-claims
Sep 5, 2026
Merged

Recorded diff shapes become self-checking, and a false cost claim comes out#502
derek73 merged 23 commits into
masterfrom
claude/differential-stale-claims

Conversation

@derek73

@derek73 derek73 commented Sep 3, 2026

Copy link
Copy Markdown
Owner

The recorded diff shapes that test_the_recorded_rule_still_wins_each_contested_name had been feeding classify() move into tools/differential/compare.py as _RECORDED_DIFFS, and the run now verifies them. Four of the 35 rows turned out to be wrong on arrival. Along the way, the cost claim that decided a neighbouring decline turned out never to have been measured.

Closes #497. docs/design/decisions.md gains the seventh ledger arc entry, which is the distilled record; this body is the change, not a second copy of it.

What changed

_CROSS_RULE_WINNERS (tests/v2/test_ledger_guards.py) recorded a (name, shape) -> winning rule mapping and handed the shape to classify() as an input. Nothing ever compared that shape to a shape a run makes, so a wrong one that still routed to the same rule stayed green forever — the half only a run can do, because main() has measured every name's real diff by the time it asks and the unit suite installs no wheel at all.

So the shapes move to where both halves exist. _CROSS_RULE_WINNERS keeps what it is for — which rule wins a contested name — and reads the shape from compare._RECORDED_DIFFS. The pair (name, shape) -> winner reconstructs the old dict exactly for every surviving row: no winner moved and no shape changed (checked by parsing both versions and reconstructing, not by reading the diff).

One roster, two checks, two placements, and the asymmetry is the decision rather than where the code fit:

  • The absent-name half — a recorded row naming a string no corpus holds any more — refuses pre-worker, beside vacant, because it reads the ledger and the loaded names and nothing the worker produces. It reads the pre-skip corpus_names, because "does any corpus still hold this name?" is a question about the files. Reading the post-skip list instead would tell a contributor to delete a live row; measured at 1.4.0 the two lists differ by three names.
  • The measured half prints a MOVED SHAPE block and feeds the exit code, like over_declared_rules. It reads the post-skip list, because "was this name compared?" is a question about the run. It cannot raise mid-report: a raise there would precede dormancy, OVER-DECLARED, UNEXPLAINED and the radar block, so a stale roster row would hide an unexplained diff — the gate's primary output. Measured, a --corpus tools/differential/corpus.jsonl run at 1.4.0 prints 62 EXPLAINED NOTHING lines, and the corrupted copy below prints all 62 and the MOVED SHAPE block; a raise would have printed neither.

A row with no measured shape has two causes — the parser stopped moving the name, or it is compared under a declared order alone — and the two hand the check byte-identical arguments, so the message names both and asserts neither. The README's MOVED SHAPE bullet is corrected for the same reason: it kept "the parser changed what the name does", which is false in the case that motivated the check.

The finding: the worker pass was never multi-minute

"The multi-minute worker pass" entered on 2026-08-05 in 7767ba2, at one site, with no measurement recorded beside it, and was copied outward until eight sites carried it (git grep -n -i minute 514a2a1): four in tools/differential/compare.py (one of them spelling it "costs minutes", so count the claim and not the wording), three in tests/v2/test_differential.py, and a Declined: bullet in docs/design/decisions.md that gave it as "the whole of the reason" for declining precise per-name contest detection.

Measured 2026-09-03 over the whole corpus:

baseline 1.4.0   0.34s wall     worker pass 0.10s
baseline 2.0.0   0.46s          worker pass 0.24s
baseline 2.1.0   0.47s          worker pass 0.26s
baseline 2.2.0   0.55s          worker pass 0.29s
all four back to back            1.97s

The worker pass is a fraction of a second and a whole gate run is seconds, where the phrase said minutes. Read the class, not the digits. (Timing trap, recorded with the figures: wrap compare._run_worker in a timer and call main() — calling _run_worker directly on the loaded entries aborts at a 1.4.0 baseline, since the seven order-bearing shape-4/5 entries have to be dropped by the baseline-minimum skip first.)

Three of the eight sites were making an ordering argument — a refusal before the worker beats one after it — and that argument stands; it is restated on what is actually true, that a late refusal lands below the run's own published baseline: header for a comparison it is about to disown. The Declined bullet keeps its conclusion on coverage, which is true and was the second support all along: a run-time check sees one ledger, for one baseline, and only when somebody invokes the tool, where the static predicate covers a rule from the moment it is written.

Two things transfer past the digits. The claim was never a measurement anyone got wrong — it was a plausible sentence about a subprocess that installs a wheel, and it propagated by copying. And that is how a false number survives a month: an argument with two supports loses one and nothing downstream changes, so no reader ever meets a contradiction.

Removing it was itself hard to do cleanly — review of the commit that removed the phrase found four instances of the very defect that commit exists to remove (figures living verbatim in two dated homes with nothing linking them; a baseline-specific figure quoted at a site that names no baseline; a bullet still selling the static check on the cheapness it had just retired). Fixed in 40c82a7.

What the mechanism caught on arrival

Re-measuring the 35 rows to write the provenance note found four that a run contradicts — two names across the two 2.x ledgers, so two rows each — all four added by #452's work (ee6aaa6, PR #455) under two explicit claims of measurement.

They were deleted rather than corrected, because neither name pins a contest, which is what the roster is for:

  • 'Nguyen, Van' diffs at no baseline at all — compared under the default order every time, the tree agreeing with all four wheels — so no run ever asks classify() about it. That is 'Doe,, Jr.''s position, which the 1.4.0 roster gives no row for the same reason.
  • 'Jane née and Jones Smith' diffs {family, given, maiden, middle} at 1.4.0/2.0.0/2.1.0 and not at all at 2.2.0, never the {family, maiden, middle} recorded. Its shape was correctable and the rows went anyway: the string is a malformed harvest from corpus_issues.jsonl (radar) whose right parse nobody can state.

The general shape, which is the part worth keeping: a row pinning a race with one runner is never exercised as a contest, so its shape only ever had to agree with itself. At the shape each of these names really produces, exactly one rule admits it. That is why nothing caught them, and it is not a fact about two names.

"Only one rule admits it" is not on its own grounds to delete a row — 13 of the 31 surviving 1.4.0 rows are in that position too and stay, because the shapes they pin are shapes runs actually make. Deleting a row removes a pin, not a name; no corpus changed.

Both 2.x sections stay as explicit empty mappings — the completeness guards require it — and their comments now say emptiness is a position, not an oversight, and say what it is not: measured, 5 of the 247 diffs at 2.0.0 and 1 of the 155 at 2.1.0 do move a shape two or more rules admit, and nobody has argued one of those boundaries yet. #501 asks whether that should change now those six are measured rather than unexamined, and the comments cite it.

Why a shape may be pinned where a count may not

This is what keeps the roster affordable, and it is the line between this and #452's declined explains = N. A diff shape is one name under two parser versions, so adding a name to a corpus cannot move it; an explained-name count moves on every corpus edit. Shapes drift only when the parser changes those names — so the drift and the reason to re-read the roster are the same event, which is exactly what a recorded number usually fails to be.

The prose sweep, and what its own scan got wrong

#451's criterion sorts the stale claims into three treatments, chosen by what the count is doing rather than by how wrong it is: a past-tense count that is the finding stays; a count standing beside the enumeration it counts loses the digit, because the list is self-checking and the digit is the only part that can rot; a live exhaustiveness claim with nothing recomputing it is regrounded on the property it was reaching for. Corrected examples: a widening described as claiming "644 of the 654 unclassified corpus names" where the same expression claims 994 of 1008 today; "all five corpora" where six corpus*.jsonl are on disk and a run globs them all; _MUST_NOT_MATCH's "Six such widenings" over a list of four; "all 31 the regex claims" where it claims 32.

The scan that scoped the sweep was wrong three ways, and it is the scan rather than the three lines it missed that the next sweep will reuse:

  1. Two of its 19 machine-readable hits were false positives — a version number, and a hypothesis rather than a measurement — so the real population was 17.
  2. Its file list omitted expected_since_2.2.0.toml, which held a further copy of a claim it was already treating in two other ledgers, so the sweep would have shipped two of three siblings corrected.
  3. Its predicate matched only lines beginning with #, which made every docstring invisible to it. That is how _reaches_non_vocabulary's figures — taken against a 751-name corpus, against 1116 distinct names today — survived the first pass. A docstring pass found one further stale digit and confirmed three others correct.

Three of the four were fixed in a follow-up commit rather than the first, because the first sweep identified and skipped them; leaving them would have shipped a sweep that skipped known instances of its own issue.

Verification

uv run pytest -q: 6480 passed, 156 skipped, 5 xfailed. uv run ruff check . and bare uv run mypy (111 source files) clean.

The gate at all four baselines, exit 0 at each:

baseline corpus intentional unexplained radar unclassified MOVED SHAPE
1.4.0 1113 names (7 skipped) 352 0 0 none
2.0.0 1120 247 0 0 none
2.1.0 1120 155 0 0 none
2.2.0 1120 14 0 0 none

No rule moved. Parsing all four ledgers at 514a2a1 and at HEAD and comparing structurally — not grepping — the rule count and the positional issue order are identical in each (75 / 66 / 59 / 1), and no name_regex, fields or orders value differs. The ledger changes in this branch are comment-only.

The new check is not vacuous. Corrupting one _RECORDED_DIFFS row in a scratch copy ("Andrews, M.D.": ("given", "suffix")("given", "middle")) and re-running at 1.4.0 gives exit 1 and, appended after a report that is otherwise byte-identical to the clean run:

MOVED SHAPE expected_since_1.4.0.toml: 1 recorded diff shape(s) disagree with this
run. Each is a FINDING, not a number to update: the winner pinned beside the shape
in _CROSS_RULE_WINNERS (tests/v2/test_ledger_guards.py) was recorded for the OLD
shape, so read both before editing either.
  'Andrews, M.D.'
    _RECORDED_DIFFS records ['given', 'middle']; this run measured ['given', 'suffix']

Under --corpus tools/differential/corpus.jsonl the same corrupted copy prints all 62 EXPLAINED NOTHING lines and the block, which is the placement the check was moved for.

The unit suite is still hermetic. tests/v2/test_differential.py's header says nothing there spawns uv or the network; proved by running both differential modules with subprocess.Popen, subprocess.run and os.system replaced by a raising stub — 269 passed. (Six tests do call the real _run_worker, under a faked Popen; the loose paraphrase "every test monkeypatches _run_worker" was itself one of the claims this branch corrects.)

Related

🤖 Generated with Claude Code

derek73 and others added 18 commits September 3, 2026 00:34
…t multi-minute

"The multi-minute worker pass" entered on 2026-08-05 in 7767ba2 at one
site, with no measurement recorded beside it, and was copied outward
until eight sites carried it -- four in compare.py, three in
test_differential.py, and a Declined bullet in decisions.md that gave
it as "the whole of the reason" for declining precise per-name contest
detection.

Measured 2026-09-03 over the whole corpus: a run is 0.33s wall at
baseline 1.4.0 and 0.57s at 2.2.0, the default, of which the worker
pass is 0.10s and 0.29s; all four baselines back to back total 1.97s;
a run with uv's cache emptied first, so the pinned wheel is
downloaded, is still under a second. Wrong by orders of magnitude.

The three compare.py comments and the two test docstrings that used
the phrase were making an ORDERING argument -- a refusal before the
worker beats one after it. That argument stands and is kept; it is
restated on what is actually true, that a refusal after the worker
lands below the run's own `baseline:` line, having already published
a comparison the run was about to disown. The Declined bullet is
rewritten to rest on COVERAGE, which is true: a run-time check sees
one ledger and only when somebody invokes the tool, while the static
predicate covers a rule from the moment it is written. Every figure
carries its date and a recompute.

Prose only -- no code, rule or test behavior changed.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s, not two

Review of 4844c2e caught four instances of the defect that commit
exists to remove.

The figures lived verbatim in both compare.py and decisions.md, both
dated, with nothing linking them -- the ledger's own "two copies means
one gets updated and the other does not". decisions.md keeps the full
set, both recompute recipes and the _run_worker timing trap, since
AGENTS.md puts dated measurements there by convention; the compare.py
block is reduced to the argument (order, not clock), the class claim,
and a citation by heading, as compare.py already cites decisions.md#H1
and "the initials view" elsewhere.

The same block claimed to be where "multi-minute" entered. git log -S
says otherwise: 7767ba2 seeded it in validate_rules' docstring and
016fead copied it here the same day, which the block's own opening
concedes ("validate_rules' own reasoning applies"). Claim dropped
rather than restated.

"The worker pass is a tenth of a second" was the 1.4.0 figure quoted
at a site that never names a baseline; at the 2.2.0 default it is
0.29s. Now "a fraction of a second", which is true at every baseline.

The bullet's closing sentence still sold the static check on
"cheapness" -- the very axis the bullet had just retired. Dropped.

Also: two sites described the counterfactual late refusal as one that
had published a comparison. At that position only corpora:/tree:/
baseline: have printed and the classify loop has not run. What is
really spent is a wheel installed and the whole corpus parsed for a
comparison that will never be made, under a published baseline:
header. The twin at the tree check was accurate and is left alone.

Gate re-run at all four baselines: unexplained 0, exit 0 at each.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… keep

Dropping the clause also dropped the comma that made the trailing
participle work, so the sentence read as 'reports the fact of having
published' rather than 'reports below its own baseline: line'. The
anchor is true at that position -- baseline: prints after _run_worker
and before the classify loop -- and both twin comments keep it.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The roster states a shape to ask which rule wins, then feeds that shape to
classify() as an input -- so a guessed one agreed with itself forever. This
is the half only a run can do. Pure function here; the data and the wiring
follow.

Two of the tests are written against a mutation that survived their first
draft: the order-bearing row goes SECOND (a last-wins read of every row
would otherwise agree by accident) and the matching-shape row is asserted
twice, once with the recorded tuple spelled out of order, since nothing
else pinned that both sides are sorted before they are compared.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review found a survivor: the first fixture pins that a LAST-wins read of
every row breaks, and the plan's pinned that a FIRST-wins one does, so
neither alone refuses a check that simply drops the order filter. Both are
reachable -- `diffing` is appended in corpus file order, so a name's two
entries arrive either way round. The second fixture is the order-bearing
row alone: recorded, compared, and no default-order comparison of it, where
the honest answer is `measured` None.

The measured side's sort now has an instrument too. It cannot be refuted
outright, since a set CAN iterate alphabetically -- the two-role set does
under 85 of PYTHONHASHSEED 0..199 -- so the row uses all seven roles, which
did under none of them.

Also: the `measured` field comment named _RECORDED_DIFFS, which no line in
the tree defines yet, and says `the roster` instead.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…t check

Three promises the docstring was making loosely, where Task 4 has to read
it as a contract.

`compared` is now named: the entry list AFTER the baseline-minimum skip,
not the pre-skip `corpus_names` the contest checks read on purpose. The
nearest wrong variable sits two screens up, and taking it would report a
name skipped at an old baseline as a name that stopped diffing. Figures
re-measured today at --baseline 1.4.0: 1120 load, 7 skip, 1113 compare.

The #382 citation claimed more than it did. vacant_exemptions' caller
NOTEs under --corpus and REFUSES under a full run; this function is silent
in both, so a recorded name no corpus holds any more is forgiven forever --
the shape #497 is itself about. Said plainly, with the missing half named
(`set(recorded) - compared` under `full_corpus`) and left to the caller.

`measured` None has two states behind it -- the parser stopped diffing, or
the name is compared under a declared order alone -- and they are collapsed
on purpose: separating them means reading the order-bearing rows this
function ignores by design. Documented as a collapse, with what a message
may claim and what it may not, and _Dormant's `kind` named as the shape to
copy if it ever has to be carried.

Also: the seed figure gets its recompute line, and the pair moves down
beside dormant_rules and over_declared_rules -- the other two run-fed
checks -- rather than sitting among the static ledger ones.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two corrections to what the docstring tells Task 4.

`set(recorded) - compared` was the wrong expression for "a recorded name
no corpus holds any more", and wrong by the trap the paragraph above it
warns about: `compared` is post-skip, so that difference also holds names
every corpus still has which this baseline skipped. Measured at 1.4.0,
three of them -- 'de Mesnil Jean, Dr.', 'de la Cruz Juan Carlos, Dr.' and
'de la Cruz née Vega', all order-bearing with no order-None twin, all in
corpus_shapes.jsonl throughout. `full_corpus` does not screen them: it is
_CORPUS_FLOORS against the files on disk and knows nothing about the skip.
A caller taking the prescription verbatim would refuse at the compat
baseline and tell a contributor to delete a live row -- the earlier-draft
`vacant` bug this file already records. The expression is
`set(recorded) - set(corpus_names)`, and the docstring now says why the
pre-skip list is right there while the post-skip one is right for
`compared`: one question is about the run, the other about the files.

The escape hatch offered for the collapsed `measured` None did not work.
main() appends to `diffing` only where a comparison DIFFED, so a name
compared under the default order with no diff leaves no row: a name
compared under both orders and one compared under a declared order alone
hand this function byte-identical arguments -- constructed and run. The
collapse stands and its reason is stronger, but "a caller holds `diffing`
and can take it from there" is deleted: separating the states needs the
orders each name was compared under, which is a fourth argument, and a
Task 4 author filling _Dormant-style `kind` off `diffing` would print a
confident wrong label.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… them

_CROSS_RULE_WINNERS keeps what it is for -- which rule wins a contested
name -- and takes the shape from compare._RECORDED_DIFFS, which the run
can verify. Thirty-five hand-written shapes leave the test. No winner
moves: the pair (name, shape) -> winner reconstructs the old dict
exactly.

Re-measuring them to write the provenance note found four that a run
contradicts, all four added by #452 and all four in the 2.x ledgers.
'Nguyen, Van' diffs at no baseline at all, so the {family} recorded for
it is a shape no run makes; 'Jane née and Jones Smith' diffs {family,
given, maiden, middle} at both 2.x baselines, not the three recorded
(fix(#445) wins it either way). The 31 rows at 1.4.0 all still agree.
Left standing here, where nothing but the shapes' address changes; the
note beside _RECORDED_DIFFS records the measurements and the roster's
2.1.0 block stops claiming it was measured.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two 2.x sections of _CROSS_RULE_WINNERS and _RECORDED_DIFFS held
'Nguyen, Van' and 'Jane née and Jones Smith'. Both recorded a diff shape
a run contradicts, and the wrong shape is the symptom: at the shape each
name really produces, exactly one rule admits it, so classify() was
never adjudicating a contest and the recorded shape only ever had to
agree with itself.

'Nguyen, Van' produces no diff at ANY of the four baselines -- compared
under the default order every time, the tree agreeing with all four
wheels -- so no run ever asks classify() about it. That is 'Doe,, Jr.'s
position, which the 1.4.0 roster gives no row for the same reason.
'Jane née and Jones Smith' diffs {family, given, maiden, middle} at
1.4.0/2.0.0/2.1.0 and not at all at 2.2.0, never the {family, maiden,
middle} recorded; its shape was correctable and the rows went anyway,
the string being a malformed harvest from corpus_issues.jsonl (radar)
whose right parse nobody can state.

Both sections stay present as explicit empty mappings -- the
completeness guards require it, and the comments now say emptiness is a
position, not an oversight. They also say what it is NOT: measured
2026-09-03, 5 of the 247 diffs at 2.0.0 and 1 of the 155 at 2.1.0 do
move a shape two or more rules admit. Nobody has argued one of those
boundaries here yet. And "only one rule admits it" is not on its own
grounds to delete a row: 13 of the 31 surviving 1.4.0 rows are in that
position too and stay, because the shapes they pin are shapes runs
actually make.

No corpus changes: deleting a row removes a pin, not a name.
recorded_diff_mismatches now reports zero at all four baselines; the
gate is unchanged at 352 / 247 / 155 / 14 intentional, 0 unexplained, 0
radar-unclassified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The comment states a position -- a row is owed when someone argues a
boundary, not before -- and #501 asks whether that should change now
that the six contested 2.x diffs are measured rather than unexamined.
Cite it so a reader meets the question where the position is stated.

Refs #497, #501

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes the half _CROSS_RULE_WINNERS could not check for itself: its shapes
were inputs to classify(), so a guessed one stayed green forever. A moved
shape is a finding, and the message says so.

Two name sets, one line apart and not interchangeable. The shape check reads
the POST-skip corpus, because "was this name compared?" is about the run.
The absent-name half -- a roster row naming a string no corpus holds, which
would otherwise be forgiven forever under the full gate -- reads the PRE-skip
corpus_names, because "does any corpus still hold this name?" is about the
files, and the baseline-minimum skip empties no file. Measured at 1.4.0 the
two differ by three names, all in corpus_shapes.jsonl; the post-skip reading
would wait for the first roster row on a shape-tagged name and then tell a
contributor to delete a row for a name the run just read past.

Like the vacancy check it refuses only over a full corpus, and unlike it
stays silent under --corpus, where there is no count to misread.

A row with no measured shape has two causes -- the parser stopped moving the
name, or it is compared under a declared order alone -- and the two hand the
check byte-identical arguments, so the message names both and asserts
neither.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two placements, both wrong for the same reason the file already argues
elsewhere.

The absent-name half needs the ledger and the loaded names and nothing the
worker produces, so it belongs beside `vacant`, pre-worker -- a refusal after
the wheel is installed and the corpus compared prints below the run's own
`baseline:` header for a comparison it is about to disown (#382). Moved. The
post-skip list is not even in scope there any more, which is the placement
paying for itself.

The measured half is the opposite case and stays after the comparison, but it
cannot raise there: the raise landed MID-report and took dormancy,
OVER-DECLARED, UNEXPLAINED and the radar block with it. Measured, a `--corpus
corpus.jsonl` run at 1.4.0 prints 62 EXPLAINED NOTHING lines and 0 of them
with one _RECORDED_DIFFS row corrupted -- a stale roster row hiding an
unexplained diff, which is the gate's primary output. It now prints and feeds
the exit code, like over_declared_rules, its structural sibling.

Also: the header sentence claimed "the parser changed what the name does",
which is false for the second cause of an unmeasured row. Dropped; the
FINDING instruction, true either way, stays.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e message

Review fixes, all convention rather than design.

The recompute one-liner beside `gone` did not run: `_RECORDED_DIFFS[ledger]`
keys a dict of file names with a Path, one line above code that spells it
`ledger.name`. Corrected and run; the figures it prints (18 to 30 of 31, 30
for corpus_shapes.jsonl) are unchanged.

The three-name measurement had reached three copies and had already drifted
-- the docstring ends the analogy "one baseline over" and the new comment
said "one check over". This file argues against exactly that 60 lines below,
so the docstring keeps it and the other two point there. One sentence stays
in main() because it exists nowhere else and is an argument about the
placement rather than about the function: none of the three names carries a
roster row today, so the post-skip reading would refuse nothing yet.

The departed-name refusal named neither file, while telling the reader to
delete a row "beside" another in a different one, under a `{ledger.name}:`
prefix pointing at a .toml where no repair happens. It now names
compare.py and test_ledger_guards.py, and leads with the question that
picks the repair -- did the name leave deliberately, `git log -S` over the
corpora -- since both repairs can be right for one name.

"THREE CHECKS READ THE NARROWING AT THREE DIFFERENT STRENGTHS" was four,
in compare.py and in README.md, with the fourth arguing its strength 95
lines below the count. Both amended, and the README gains the MOVED SHAPE
section it had for every peer check.

MOVED SHAPE hoists its invariant text into a one-time block header instead
of reprinting ~45 words per row, following the Role-vocabulary legend rather
than OVER-DECLARED's one-sentence NOTE. The two-cause disclaimer is
one-time AND conditional: a block whose rows all carry a measured shape
should not print advice for a case that did not occur.

Nits: full_corpus serves two checks now, not one; the exit-code comment
counted four ways where the return has five terms, and never named
NO LONGER DORMANT; `recorded`'s binding site is now half a clause away.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The code header dropped 'the parser changed what the name does' because
two causes reach a moved shape and the check cannot separate them. The
README bullet kept it -- and the claim is false in the case that
motivated the check: the four rows #497 deleted recorded shapes no run
makes, wrong when written rather than overtaken by a parser change.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sorted by decisions.md's own criterion: a past-tense count records a fixed
state and stays; a count standing beside the names it counts is redundant
with a self-checking list; a live exhaustiveness claim with nothing behind
it is regrounded. 'This rule now claims exactly one name' measured eight.

Also fixes the three "all five corpora" claims routed here by the review
of Task 1: there are six corpus*.jsonl on disk, and a run globs them all,
so the corpus count was never the argument.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follow-up to 22e3b24, which flagged these and left them. They are the
issue's own class and leaving them shipped a sweep that skipped known
instances.

_reaches_non_vocabulary's docstring: figures taken against a 751-name
corpus (634/751, 542, 592, 783 lines) against 1116 distinct names from
1251 lines today. Past-tensed the demonstration and dropped the digits:
the finding is that a member unconstrained outside one 3-character
window walks past every hand-picked probe, which no count strengthens,
and the eight-string tuple it describes no longer exists. The rule's
own claim figure is not restated because it could not be reproduced --
a replacement digit nobody can re-derive is the defect, not the fix.

_MUST_NOT_MATCH's preamble said "Six such widenings" over a list of
four. The enumeration is the evidence and is self-checking; the count
was the only part that could be wrong, and nobody can recover which
two are missing.

fix(#274)'s ledger comment said "Five claimed names" with five
enumerated beside it, and walked "all 31 the regex claims" where the
regex claims 32 today -- a live count inside the very paragraph that
names stale counts as this file's standing hazard.

Three corrections to the sweep's own scope, for the record:

- the step-1 scan's 19 matches included TWO false positives -- a
  version number at expected_since_1.4.0.toml's fix(leading-credential)
  note ("explained 2.0 routing"), and a hypothesis rather than a
  measurement at _MUST_NOT_MATCH ("a rule ... that claims exactly ONE
  corpus name CAN BE widened"). The real machine-readable population
  was 17.
- the scan's file list omitted expected_since_2.2.0.toml, which carried
  a 20th copy of the fix(#462) claim. Treated with its two siblings.
- the scan's predicate matched only lines starting with '#', so every
  docstring claim was invisible to it. That is how the
  _reaches_non_vocabulary figures survived. A docstring pass over both
  guard files found one further stale digit (_carries said the
  isascii() split covers "12 of the 16 entries" where MAIDEN_MARKERS
  ships 17), fixed here by naming the predicate instead of counting it.
  Three other docstring counts measured correct and stay: _carries' own
  two/eight, which already carry a runnable recompute; the 256 shapes,
  which is 2**8 - 1 role subsets plus the standalone _initials that
  validate_rules refuses beside any other field; and order_contests'
  1350/657/2, dated and carrying a recompute recipe.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The seventh differential-ledger arc entry: recorded diff shapes move
into the tool and the run verifies them; why a shape may be pinned
where a count may not; the four rows that pinned a race with one
runner; the prose sweep and what its own scan got wrong; three
declines.

The headline finding is the arc's own subject arriving in its own
record: "the multi-minute worker pass" was never measured, and it was
under TWO decisions rather than one. The declined run-time contest
check kept its conclusion on a new argument (coverage); the
pre-worker placement of the absent-name refusal kept its ORDER while
losing its reason, which is re-argued here on the published-header
ground. The entry says which support was doing the work in each case.

One home per fact throughout. The timing figures, the eight-site
census, the 62-lines-to-0 placement measurement, the 5-of-247 /
1-of-155 contested 2.x diffs, the 13-of-31 one-runner survivors, the
per-name deletion measurements and the corpus tier split are CITED
where they already live -- the rule-order arc's Declined bullet,
compare.py's _RECORDED_DIFFS provenance note and
recorded_diff_mismatches' docstring, and _CROSS_RULE_WINNERS' 2.x
section note. #451's rule applied to the claim that demonstrates it.

mechanisms.md#RECORDED-ROSTERS gains the limit this arc found and
amends its Lives-in list. The contract statement forbids re-deriving
the EXPECTATION from the inputs the check reads; _CROSS_RULE_WINNERS
obeyed that and still went wrong on the other side of the assertion,
because a roster row can hold a recorded INPUT and only the
expectation half is asserted. The test for a recorded input is not
"is it checked" but "would the consumer answer differently if it were
wrong". rules.md is untouched -- no parsing rule changed.

Review corrections, all measured, listed because several are this
arc's own class: "carried wrong rows for months" was five days (the
roster itself is 22 days old); "every test monkeypatches _run_worker"
is false at 168 test functions, 20 of which mention it and six of
which run the real one under a faked Popen -- the entry now states
the hermeticity claim in the form that survives measurement and says
so, the loose form standing in compare.py today; "no number at all"
in _reaches_non_vocabulary's docstring is no LIVE number; "one of the
four had a correctable shape" is one of the two NAMES; the eight-site
census and the 1116/326/790 split were restated where this file
already owns them, and now point at the owner. And the 31 rows at
1.4.0 holding is not evidence the 1.4.0 half was ever sound --
田中さん II was guessed on a genuinely contested row and was fixed by
hand in e5e4955, which is the case #497 was filed off.

Verified: 419 doc-citation/rules-doc tests, 6480 passed / 156 skipped
/ 5 xfailed, ruff and mypy clean; the gate at all four baselines
352 / 247 / 155 / 14 intentional, 0 unexplained, 0 radar-unclassified,
no MOVED SHAPE block.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… file

Both are #497's class, in the file that implements the check for it.

'every test in test_differential.py monkeypatches _run_worker' is
false -- 20 of 168 mention it at all, and the hermeticity the sentence
is defending rests on the module header plus faked Popen, not on a
universal. The claim is sound; the paraphrase of it was not.

'its five corpus files ... the other three are still bare strings' is
wrong twice: six files, four bare-string. It is the same 'all five
corpora' claim swept out of three other files, surviving because that
sweep was scoped to the ledgers and the guard modules and never
reached compare.py. Both now name the predicate rather than counting
its output.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@derek73 derek73 added this to the v2.3 milestone Sep 3, 2026
@derek73 derek73 added docs Documentation fixes and updates tooling tests labels Sep 3, 2026
@derek73 derek73 self-assigned this Sep 3, 2026
The bullet declining a general sweep gave an exact count of
number-carrying comment lines. It was right at d9c3bd8 and wrong two
commits later, because f70a0ad added two such lines -- a live count
going stale inside the entry that declines a sweep for live counts,
within one branch.

Regrounded on the argument the sentence was always making: the ratio
is four figures against 17, and the ratio is what carries it. The
recompute stays for anyone who wants the digit.

Refs #497

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.66%. Comparing base (514a2a1) to head (b3f03cf).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #502   +/-   ##
=======================================
  Coverage   98.66%   98.66%           
=======================================
  Files          45       45           
  Lines        3219     3219           
=======================================
  Hits         3176     3176           
  Misses         43       43           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

derek73 and others added 3 commits September 3, 2026 12:20
A name can sit in a corpus file AND outside the compared set: the
baseline-minimum skip drops an order-bearing entry the baseline cannot
honor. A recorded row on such a name is checked by neither half --
recorded_diff_mismatches skips it as uncompared, the `gone` refusal
passes it as still-in-a-file. Measured: a wrong shape on
'de la Cruz née Vega' over the full corpus at 1.4.0 exited 0 in 375
lines naming it in none of them, and the window is co-located with the
only populated section (1.4.0 carries the 31 rows and is the only
baseline where the skip fires).

Not a refusal -- that is the thing recorded_diff_mismatches' docstring
argues against, and the argument stands. "Do not refuse" and "say
nothing" are different decisions and only the first was made, so main()
now prints a NOT CHECKED note over the window, naming the rows and
feeding no exit code.

Also pins three surviving mutations in the moved-shape report, which
every fixture missed by carrying exactly one row (assignment for the
append, a literal 1 for the count, a truncated row loop); pins the
per-ledger dispatch with a 2.0.0 run and a 1.4.0 decoy; and validates
_RECORDED_DIFFS row values in test_ledger_guards.py against the same
illegal states validate_rules refuses for a rule's `fields`, where a
repeated role and an empty shape passed the whole suite silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A five-agent review of #502 found the arc reproducing its own subject:
claims measured on one commit and falsified by the next two.

The criterion in mechanisms.md#RECORDED-ROSTERS was the worst of them.
"Where one rule alone admits a name's diff, the consumer answers the
same either way" is false 11 times out of 13 over the 1.4.0 roster --
a wrong shape routes to a DIFFERENT rule, so the assertion catches it
('Andrews, M.D.' wins fix(comma-family) at its recorded shape and
fix(comma-precomma-family) at {family}). It also contradicted
decisions.md, which says on the same day that single-admittance is not
grounds to delete a row. The restatement the review proposed --
"the same rule admits both shapes" -- is false too: file order still
decides, and 152 of the 246 such pairs route differently. What is true
is per-pair, and is what the entry now says.

Four sites carried "disowning a comparison it has just published"
after 40c82a7 retired that framing at two others. Measured print
order is `baseline:`, then a silent comparison loop, then `corpus:` --
so at the position all four describe, nothing of the comparison has
printed. The fourth (tests/v2/test_differential.py) was outside the
review's list.

Also: _RECORDED_DIFFS' keying argument cited two names in two ledgers
that b793ebc deleted; the MOVED SHAPE provenance was the last site
still naming one cause where the README, the printed block and the
roster header name two; decisions.md pointed at a compare.py note
f70a0ad had rewritten to say the opposite, and quoted a 168 that is
171; the PYTHONHASHSEED recipe built its seven-role set alphabetically
where the test builds it from V2_FIELDS, which is 1 of 200 against 0;
a ledger comment kept the retired tuple key; the OVER-DECLARED
parenthetical called a 47-word three-sentence block one sentence;
and the checklist's "digit here is checked" covered one of two digits.

Two things recorded that no site said: MOVED SHAPE feeds the exit code
without asking the tier, and 21 of the 31 rows sit on radar names --
so radar parser drift can fail the run; and the release checklist runs
three baselines against four ledgers with rules, leaving 2.1.0 without
a dormancy, over-declaration or shape check. Noted, not silently
fixed.

Gate 0 at all four baselines (352/247/155/14), 0 unexplained,
0 MOVED SHAPE, 0 NOT CHECKED.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…counted

Four of the five were true when written and false by the time they
shipped, all inside this branch.

decisions.md's "22 of 33 today" for _CROSS_RULE_WINNERS was measured at
960c6d0 and then falsified by b793ebc, the same branch's own deletion of
four rows -- fourteen lines below, the same file says so. Measured now:
31 rows, 31 distinct names, 21 radar / 10 contract. The digits go
entirely; the argument was always the MAJORITY, which survives them, and
a recompute replaces the count.

compare.py's "375 stdout lines with no mention of the name" is the
pre-9360919 side of a measurement whose post side sits four lines below
it, in the code implementing the note. decisions.md was corrected for
exactly this in 02ecdd5 and compare.py was not. Re-measured 2026-09-03:
378 lines, exit 0, the NOT CHECKED note naming the name; both sides now
stated with the commit between them.

README's "the one of the three that does not consult full_corpus" counts
one where two do not: only the departed-name refusal reads the flag.
MOVED SHAPE reads no flag and its docstring says so -- measured, a
--corpus corpus_issues.jsonl run at 1.4.0 with a corrupted shape on
'Carod i' prints MOVED SHAPE and exits 1. A reader was being told a
narrowed failure is suppressed like the departed-name one.

"f70a0ad, two commits later" is one commit later; d9c3bd8 is its parent.
The sentence sends the reader into history, so the miscount defeats it.

And README's "the larger of the two" over the [[never]] exclusions named
no metric and inverts under two of them: 31 rows against 2 exclusions one
way, and 21 radar names pinned against 60 corpus names those two
patterns reach (37 radar) the other. The comparative goes; both
measurements stay, dated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The note recorded that the checklist runs three commands against four
rule-bearing ledgers and changed nothing a releaser executes, so the gap
had no owner and each sweep re-found it. #503 is where the fix gets
decided; the note now cites it and says what it weighs.

Two things the note did not carry. Which checks 2.1.0 actually misses:
dormancy and over-declaration are live gaps, while the shape check is
latent only because that ledger's _RECORDED_DIFFS section is empty --
and #501, open, measured six contested diffs across the two 2.x ledgers
with no pin, one of them at 2.1.0, so the first row to land there is
checked by nothing a releaser runs. And that cost is not the obstacle,
measured rather than assumed: 0.43s at 1.4.0, 0.56s at 2.2.0, 1.97s for
all four back to back, with the recompute in decisions.md.

The three options stay #503's to choose between. Option 2's local
precedent is named because it is already settled elsewhere in the tool:
compare.py's --corpus default is deliberately a glob, on the ground that
"one that has to be asked for by name is one that stops being run",
which is this note's failure one argument up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@derek73
derek73 merged commit 446b885 into master Sep 5, 2026
11 checks passed
@derek73
derek73 deleted the claude/differential-stale-claims branch September 5, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation fixes and updates tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Measured claims in the differential comments go stale unnoticed (a reviewer read "751 names" as fact; the corpus holds 1116)

1 participant