Recorded diff shapes become self-checking, and a false cost claim comes out - #502
Merged
Conversation
…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>
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 Report✅ All modified and coverable lines are covered by tests. 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. 🚀 New features to boost your workflow:
|
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>
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.
The recorded diff shapes that
test_the_recorded_rule_still_wins_each_contested_namehad been feedingclassify()move intotools/differential/compare.pyas_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.mdgains 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 rulemapping and handed the shape toclassify()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, becausemain()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_WINNERSkeeps what it is for — which rule wins a contested name — and reads the shape fromcompare._RECORDED_DIFFS. The pair(name, shape) -> winnerreconstructs 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:
vacant, because it reads the ledger and the loaded names and nothing the worker produces. It reads the pre-skipcorpus_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.MOVED SHAPEblock and feeds the exit code, likeover_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,UNEXPLAINEDand the radar block, so a stale roster row would hide an unexplained diff — the gate's primary output. Measured, a--corpus tools/differential/corpus.jsonlrun at 1.4.0 prints 62EXPLAINED NOTHINGlines, and the corrupted copy below prints all 62 and theMOVED SHAPEblock; 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 SHAPEbullet 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 intools/differential/compare.py(one of them spelling it "costs minutes", so count the claim and not the wording), three intests/v2/test_differential.py, and aDeclined:bullet indocs/design/decisions.mdthat gave it as "the whole of the reason" for declining precise per-name contest detection.Measured 2026-09-03 over the whole corpus:
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_workerin a timer and callmain()— calling_run_workerdirectly 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 asksclassify()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 fromcorpus_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 sixcorpus*.jsonlare 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:
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.#, 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 bareuv run mypy(111 source files) clean.The gate at all four baselines,
exit 0at each:MOVED SHAPENo rule moved. Parsing all four ledgers at
514a2a1and at HEAD and comparing structurally — not grepping — the rule count and the positional issue order are identical in each (75 / 66 / 59 / 1), and noname_regex,fieldsorordersvalue differs. The ledger changes in this branch are comment-only.The new check is not vacuous. Corrupting one
_RECORDED_DIFFSrow 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:Under
--corpus tools/differential/corpus.jsonlthe same corrupted copy prints all 62EXPLAINED NOTHINGlines 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 spawnsuvor the network; proved by running both differential modules withsubprocess.Popen,subprocess.runandos.systemreplaced by a raising stub — 269 passed. (Six tests do call the real_run_worker, under a fakedPopen; the loose paraphrase "every test monkeypatches_run_worker" was itself one of the claims this branch corrects.)Related
MD, PHDgoes to whichever rule comes first) #501 — filed from this work: six contested 2.x diffs have no pin and the empty rosters read as if none existed. Cited from the comments that state the position.fix(#271/#272/#298)is the outlier by contract reach (80 = 24.5% against their 19.6%) — so a ceiling on total reach names the wrong rule as the biggest hazard.🤖 Generated with Claude Code