test(reconcile): assert the move cutoff's behaviour, not only its value (#673) - #708
Draft
willhea wants to merge 1 commit into
Draft
test(reconcile): assert the move cutoff's behaviour, not only its value (#673)#708willhea wants to merge 1 commit into
willhea wants to merge 1 commit into
Conversation
…ue (#673) The move/no-move decision compares word similarity against MOVE_THRESHOLD (0.6). A test pinned the constant; nothing tested a pair near it. The nearest case sat at 0.6667, 11.1% clear on the high side, and both low-side cases scored 0.0000 -- zero word overlap, so each would pass for any cutoff in (0, 1]. The number was pinned and the behaviour the number exists to produce was not. Three tests, all against REAL corpus text rather than fixtures composed to hit a number. The near-threshold population was re-derived here by scoring the unmatched population of all 27 committed manifest pairs through production's own round-1 -> round-2 handoff: 12 pairs land on exactly 0.6, 38 in [0.58, 0.60), 36 in [0.60, 0.62). A fixture written to reach a similarity encodes the author's belief about the scorer and then tests the belief; these are quoted from named bills so a reader can re-measure. - test_a_pair_at_exactly_the_cutoff_is_moved. Two short-title provisions from 118-hr-4366 measuring exactly 0.6, where `>= MOVE_THRESHOLD` is True and `> MOVE_THRESHOLD` is False. It is the only fixture that can see the direction of a comparison MOVE_THRESHOLD's own comment describes as "at or above". - test_a_pair_just_below_the_cutoff_stays_separate. Two effective-date provisions from 119-hr-1 at 0.5957, 0.0043 below the cutoff against the existing 0.0000. - test_the_audited_pair_keeps_its_classification_split, in test_canonical_baseline.py. That third one is deliberately NOT what #673's option 2 literally proposes. The digest gate already asserts the summary counts and already prints the moved delta on failure, so "assert the counts too" was substantially already done. The real gap is that every value it compares against lives in a regenerable file and its own remedy line says to regenerate. The figures in the new test are literals in the test source: UPDATE_BASELINE=1 cannot move them, only a person editing this file can, which appears in a diff and can be argued with. Detection is shared with the digest gate; attribution and remedy are not. The full split is pinned rather than `moved` alone, because a lost move is conserved as one removal plus one addition. Verification, each mutation applied and reverted, tree confirmed clean after: - Mutation 2 (multiply the short-circuit bound so pruning is 2% too aggressive, leaving MOVE_THRESHOLD at 0.6) reddens the at-the-cutoff case. Before this change it left the entire fast tier green. Note the orientation: the effect #673 describes needs `< threshold * 1.02`; multiplying the other operand prunes LESS aggressively and the boundary test correctly stays green under it. - Mutation 2, then regenerating the baseline, is the result that justifies the third test. All 27 digest assertions go GREEN -- silenced by exactly the remedy their own failure message recommends -- while the regression is still present and test_the_audited_pair_keeps_its_classification_split remains the sole failure. - Mutation 1 (constant 0.6 -> 0.45) still reddens the existing constant pin, so the additions do not replace coverage that already works. It also reddens both new cases: the at-the-cutoff one on its precondition, saying the fixture needs re-measuring, and the below-cutoff one on the behaviour, saying a pair below MOVE_THRESHOLD was reconciled as a move. Fast suite 2011 passed / 4 skipped / 15 xfailed; the three affected modules 69 passed / 1 skipped; ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <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.
Closes #673, via options 1 and 2.
The problem
DeltaTrack decides whether a provision moved or was deleted and replaced by comparing word similarity against
MOVE_THRESHOLD = 0.6. One test asserted the constant is 0.6. Ten behaviour tests checked pairs, but the nearest sat at 0.6667 (11.1% clear) and both low-side cases scored 0.0000 — zero word overlap, so each would pass for any cutoff in (0, 1].The number was pinned. The behaviour the number exists to produce was not.
The near-threshold population, re-derived
#673 assumed real near-boundary corpus pairs existed but had not checked. Re-measured here by scoring the unmatched population of all 27 committed manifest pairs through production's own round-1 → round-2 handoff, with a probe bound below 0.6 so the region either side is visible rather than truncated at it:
So the fixtures are real, not composed to hit a number. That matters because a fixture written to reach a similarity encodes the author's belief about the scorer and then tests the belief. Both are quoted from named bills so a reader can re-measure rather than trust.
What was added
1.
test_a_pair_at_exactly_the_cutoff_is_moved— two short-title provisions from 118-hr-4366 measuring exactly 0.6, where>= MOVE_THRESHOLDis True and> MOVE_THRESHOLDis False. It is the only fixture that can see the direction of a comparisonMOVE_THRESHOLD's own comment describes as "at or above".2.
test_a_pair_just_below_the_cutoff_stays_separate— two effective-date provisions from 119-hr-1 at 0.5957, which is 0.0043 below the cutoff against the existing 0.0000.3.
test_the_audited_pair_keeps_its_classification_split— see below, because it is deliberately not what option 2 literally proposes.Option 2, re-aimed
Reading the existing gate closely:
assert actual == expectedalready asserts the summary counts, and the failure message already printsmoved: 163 -> 161. So "assert the counts as well as the digest" was substantially already done, and building it again would have been inert.The real gap is the one #673's own framing points at: every value the digest gate compares against lives in a regenerable file, and its remedy line says to regenerate.
The figures in the new test are literals in the test source.
UPDATE_BASELINE=1cannot move them; only a person editing that file can, which appears in a diff and can be argued with in review. Detection is shared with the digest gate — attribution and remedy are not.The full split is pinned rather than
movedalone, because a lost move is conserved rather than destroyed: it becomes one removal plus one addition.Verification
Each mutation applied and reverted, tree confirmed clean afterwards.
Mutation 2 (short-circuit prunes 2% too aggressively;
MOVE_THRESHOLDuntouched) reddens the at-the-cutoff case. Before this change it left the entire fast tier green.Mutation 2, then regenerating the baseline — the result that justifies test 3:
UPDATE_BASELINE=1runThe digest gate goes green after exactly the remedy its own failure message recommends, while the behavioural regression is still present. The new test is the sole survivor.
Mutation 1 (constant 0.6 → 0.45) still reddens the existing constant pin, so the additions do not replace coverage that already works. It also reddens both new cases, and the split is deliberate: the at-the-cutoff one fails on its precondition saying the fixture needs re-measuring, and the below-cutoff one fails on the behaviour saying a pair below
MOVE_THRESHOLDwas reconciled as a move.Gates. Fast suite 2011 passed / 4 skipped / 15 xfailed; the three affected modules 69 passed / 1 skipped;
ruff checkandruff format --checkclean.Caveat: measured in a worktree lacking the gitignored fetched corpus, which reports 4 skips where the main checkout reports 1. None relate to this change. The corpus figures above come from the committed manifest, which that gap does not touch.
Redundancy, flagged rather than acted on
The house rule says to check whether a new test makes an older one redundant. Two candidates, which I have not removed because deleting coverage was not part of the ask:
test_dead_zone_pair_becomes_moved(0.6667) is now largely dominated. Any threshold raise that reddens it also reddens the at-the-cutoff case, which fires strictly earlier. It still carries the 0.4 → 0.7 → 0.6 history in its docstring, which is the main reason to keep it.test_below_threshold_unchangedandtest_low_similarity_stays_separateboth sit at 0.0000 and are redundant with each other before this change; the new below-cutoff case dominates both.Happy to drop any of these in a follow-up if you agree.
Still open from #673
SIMILARITY_THRESHOLD(0.4, the round-1 correspondence cutoff) has the same gap. This change measured the move cutoff only.