Alignment tuning against the benchmark: move recall and correspondence F1 - #158
Open
houfu wants to merge 1 commit into
Open
Alignment tuning against the benchmark: move recall and correspondence F1#158houfu wants to merge 1 commit into
houfu wants to merge 1 commit into
Conversation
The #143 report measured the exact pass taking the first of several equally exact candidates in document order, and named the cost: 84 of 1,349 scored exact matches wrong, and F1 between 0.03 and 0.35 on the four repetitive-schedule pairs. The mechanism is one sentence -- an edited test block matches no key, so it consumes no source, and every identical pair below it is off by one. The exact, label and fuzzy passes now break a tie on the structurally nearest candidate before falling back to document order, and the exact pass's assignment across one bucket is order-preserving so two exact pairs can never cross and invent a move between them. Nearness is the plain difference of two sibling positions in whole blocks: scaling the two sides to a common length was tried and rejected on measurement, because it assumes a group grew evenly and sends govinfo-hr4668's section 6 to test section 16 rather than section 7. label_min_similarity goes from 0.35 to 0.50, which is the middle of the 0.37-0.65 plateau the sweep found rather than its edge. 0.35 let the label pass claim msa-markdown-move-heavy's two clause 7.1s at 0.3636 -- consuming a clause whose byte-identical partner had moved to 5.4, so the move pass never saw either. Every other default was swept and is already at its best value. Synthetic tier, difflib: correspondence F1 0.9412 -> 0.9750, so PRD section 10's 0.95 gate is met and its strict xfail is gone; move recall 0.7391 -> 0.7826 with precision still 1.0000; renumber recall 0.9795 -> 0.9932. The hand tier is unchanged at 1.0000 with the same three unreviewed moves. The change-tree goldens change only in the recorded config value; no node, matched_by or confidence moved. Move recall does not reach ADR-0009's 0.90 and cannot on this corpus: five of the 23 labelled moves are byte-identical paragraphs among 30 byte-identical siblings, where 30 candidates are equally good and reporting one would be a guess. 18/23 is the ceiling that keeps move precision at 1.0000, and the gate stays red rather than being bought with a loosened threshold. Co-Authored-By: Claude Fable 5.1 <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 first tuning pass against the benchmark ADR-0021 built for it. One rule changed in the aligner, one default moved, nothing changed in the benchmark's measurement.
Before / after
Synthetic tier,
difflibbackend, all labelled blocks. The hand tier is 1.0000 on every column before and after.xfail(strict=True), mark removed)exactwrong matchesPer plan, synthetic, F1 and move recall:
heavylightmixedmove-heavyrenumber-stormstructuretableThe four pairs that carried almost all of the loss:
repetitive-schedule-heavyrepetitive-schedule-lightrepetitive-schedule-mixedrepetitive-schedule-move-heavymsa-markdown-move-heavyWhat changed, and why
1. A structural-nearness step in the tie-break (
redlines/alignment.py). ADR-0032 said "highest score, then equalrole, then document order". Document order is right until a bucket holds more than one equal, and the report found the case where it is not: a schedule of thirty byte-identical paragraphs puts all thirty in oneexactbucket, an edited test paragraph matches no key and so consumes no source, and every identical pair below it is off by one. That single rule was the report's 84 wrong exact matches.The order is now: highest score, then equal
role, then the structurally nearer candidate — the one whose sibling position is closest, in whole blocks — then earliest source, then earliest test. It applies inexact,labelandfuzzyalike. Two things make it safe rather than clever:roleis.exactbucket the assignment is order-preserving, so two exact pairs can never cross each other and produce a move on no evidence.Distance is integer arithmetic on sibling positions, never a float, so determinism is unchanged and the tie-break is total.
Scaling positions to a common length was tried and rejected on measurement, and the rejection is recorded in the ADR: it assumes a sibling group grew evenly, and on
govinfo-hr4668-ih-to-rh— ten sections becoming twenty by insertion in one place — it sends source section 6 to test section 16 rather than test section 7, costing four hand-tier correspondences and inventing seven moves on a tier that had been perfect. Absolute distance says "as near as it was", which is the weaker and truer claim.2.
label_min_similarity0.35 → 0.50. The one default that moved. 0.35 rejected the sample pair's 0.20 case, and the report found the case it was too low for: inmsa-markdown-move-heavy, clause 7.1 moved to 5.4 with byte-identical text while a different clause took the number 7.1. The two 7.1s score 0.3636 — just over the old floor — so thelabelpass consumed both blocks and the move pass never saw the perfect cross-scope match. The sweep is flat from 0.37 to 0.65 on both tiers; 0.50 is the middle of that plateau rather than its edge.3. Every other default was swept and left alone.
fuzzy_min_similarity,positional_min_similarity,move_min_similarity,move_tie_margin,move_min_tokens,move_kinds,fuzzy_window,table_fuzzy,max_comparisons— each one across the whole committed corpus. None of them is suppressing a true move: loosening any move knob bought zero synthetic moves and cost the hand tier correspondences and new unreviewed moves, which is ADR-0009's asymmetry working.budget_exhaustedis false on every pair of both tiers.4. ADR-0032 amended in place, dated 2026-09-05. The tie-break paragraph and the
label_min_similarityrow are rewritten where they stand; a new Amendment, 2026-09-05: what the benchmark said section records the before/after, the scaled-position alternative and its rejection, and the move-recall ceiling. The Revisit when section is updated to point at it.5.
benchmark/REPORT.mdnarrative updated (benchmark/report.py) — its two now-stale paragraphs said the exact pass "takes the first" and blamed move recall on the move-heavy plan. Both are rewritten to say what is actually true, including the ceiling. Numbers and metric definitions are untouched: the benchmark was not edited to flatter the engine.6. Three new regression tests in
tests/test_alignment.py, one per property: an edited block does not shift the identical pairs below it, equally exact candidates never cross, and nearness is absolute rather than scaled. No existing test was removed or weakened.Which goldens changed, and which nodes
No change-tree node changed. No
matched_bychanged. Noconfidencechanged.tests/corpus/sample_pair/expected/change_tree.contract.jsonandchange_tree.markdown.jsonwere regenerated withtests/corpus/sample_pair/regenerate.py. Each file has exactly two changed lines, both the same recorded configuration value in two places —config.alignment.label_min_similarityand the top-levelalignment.config.label_min_similarity—0.35 → 0.5. Nothing else in either file moved, and the M1 block-tree goldens (source.*.json,test.*.json) are byte-identical.The eight promises were re-checked and all eight hold unchanged, on both profiles: the widened definition is one modify with one inline insert; the moved clause is one move plus a separate modify on its body; the inserted clause renumbers the two clauses below it; the cross-reference edit touches only the cross-reference span; the deleted sub-clause is one delete that keeps its label; the inserted table row is one row-level insert; the whitespace-only change produces no node; and the edit inside the repetitive schedule is found. The pair still produces exactly ten change nodes.
redlines/schemas/comparison-v2.json'sexamplesblock carries the same default and was updated with it.What is still red, and why
Move recall is 0.7826 against ADR-0009's 0.90, and it cannot reach the bar on this corpus without inventing a move. Its gate test stays
xfail(strict=True)with the reason rewritten to say so. The numbers:repetitive-schedule-mixed(1) andrepetitive-schedule-move-heavy(4). Both source documents contain exactly one distinct paragraph text, repeated thirty times — no label, no heading, no parent to tell one sibling from another. Each missed move's source block is one of 30 byte-identical candidates, and its true partner is one of 24 or 28 byte-identical candidates on the test side.ADR-0009 says the bar is not to be lowered quietly, and a tuning pass is not where it would be lowered. Three ways out exist and all of them are the maintainer's call, not this PR's: extend the corpus so the repetitive-schedule pairs are not 22% of the labelled moves; label those five as
unscoredon the grounds that they are unrecoverable (a change to the benchmark, which this PR was told not to make); or reopen ADR-0009 and ship moves with the number published, which is what its own Revisit when section contemplates.Two smaller things for the record:
repetitive-schedule-mixed(0.5000) andrepetitive-schedule-move-heavy(0.2000) still carry the corpus's remaining correspondence loss, for the same reason. With 30 identical siblings, nearness is the only evidence there is, and where the generator's truth is not the nearest candidate the exact pass is wrong and cannot know it. The remaining 37 wrong exact matches are all of this shape.labelpass's unique contribution on the synthetic tier is 0 — every pair it made, some other pass would have made too — while its wrong-match count is also 0 over 85 pairs. Low unique and high wrong is the cut signal; low unique and zero wrong is not, solabelstays. Noted in the ADR so the next reading has a baseline.Checks
uv run mypy redlines tests benchmarkclean.uv run pytest1637 passed, 2 skipped, 6 xfailed — including the two N2 timing tests (the new bucket walk is O(k) and adds no similarity computation; 2,000 identical paragraphs in one sibling group align in 9 ms).benchmark/REPORT.mdandbenchmark/results/latest.jsonare regenerated and committed, and the byte-stability test passes.🤖 Generated with Claude Code