push(snapshot): run-aware base/head line-correspondence (stop discarding intra-run anchors)

The correspondence mapped an equal-region base<->head line pair only when the
line's content was GLOBALLY unique in the file, so common lines (`}`, blanks,
imports) tripped `ambiguous_line_correspondence` and the finding's anchor was
discarded -> the operation came back `unmapped` -> novelty `unknown` ->
`comparison_unknown` -> the engine finding was suppressed (a direct cause of
"near-blind to fixes").

Replace the global-uniqueness gate with run-aware positional anchoring: segment
the equal-region pairs into contiguous runs (strictly +1/+1, as _equal_line_pairs
emits), and map a pair when its content occupies exactly one old run and one new
run. Duplicates WITHIN a contiguous run are positionally forced and now map;
duplicates ACROSS runs split by an edit are aligned arbitrarily by git and stay
withheld + flagged (soundness preserved -- the moved-repeated-operation guard
still passes, so no false `new`/identity claims).

Tests: update test_change_context_unknown_base_binary_and_ambiguous_lines_are_
unresolved (its two in-run sinks now anchor); keep the moved-operation soundness
test; add intra-run-maps, cross-run-withheld, and distinctive-line-anchors cases.
163 push tests pass, ruff clean.

Honest scope (design.md amendment): this removes one real cause but is
necessary-not-sufficient -- live-testing a messy 14-file Java change showed
coverage stays `incomplete` due to other, dominant blockers (capability_unavailable
until Task 8.3; the engine produced no actionable defect; eligibility/projection
gaps). Engine coverage on unseen diffs needs those too, not just correspondence.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-11 10:12:03 +02:00
co-authored by Claude Opus 4.8
parent c657c2219b
commit abb2058a54
3 changed files with 98 additions and 5 deletions
+35 -2
View File
@@ -553,9 +553,12 @@ def test_change_context_unknown_base_binary_and_ambiguous_lines_are_unresolved(r
assert "comparison_base_unavailable" in missing.unresolved_boundaries
result = SnapshotAdapter(repo).compare(comparison(base, head))
assert "declaration_paths_unavailable" in result.unresolved_boundaries
assert any("ambiguous_line_correspondence" in b for b in result.unresolved_boundaries)
# The two unchanged `sink();` lines form one contiguous equal run, so they anchor
# positionally (intra-run duplicates are forced) and are no longer ambiguous.
assert not any("ambiguous_line_correspondence" in b for b in result.unresolved_boundaries)
assert {(m.base_start_line, m.head_start_line) for m in result.line_correspondences} >= {(2, 2), (3, 3)}
# The binary file still cannot be line-corresponded.
assert any("binary_line_correspondence_unavailable" in b for b in result.unresolved_boundaries)
assert not result.line_correspondences
def test_change_context_is_bounded_and_never_executes_diff_helpers(repo: Path) -> None:
@@ -584,6 +587,36 @@ def test_change_context_moved_repeated_operations_do_not_claim_identity(repo: Pa
assert result.decode_path(result.changed_paths[0]) == "api.js"
def test_change_context_repeated_lines_in_one_run_map_positionally(repo: Path) -> None:
# Three blank lines and a sink form one contiguous equal run (only line 1 changed);
# intra-run duplicates are forced, so they all anchor without ambiguity.
base = binary_commit(repo, [(b"100644", b"a.py", b"old()\n\n\n\nsink()\n")])
head = binary_commit(repo, [(b"100644", b"a.py", b"new()\n\n\n\nsink()\n")])
result = SnapshotAdapter(repo).compare(comparison(base, head), declaration_paths=())
assert not any("ambiguous_line_correspondence" in b for b in result.unresolved_boundaries)
assert {m.base_start_line for m in result.line_correspondences} >= {2, 3, 4, 5}
def test_change_context_repeated_lines_split_across_hunks_are_withheld(repo: Path) -> None:
# 'dup()' appears in two equal runs split by the changed a/b/c lines; git's alignment
# of cross-run repeats is arbitrary, so those pairs are withheld and flagged.
base = binary_commit(repo, [(b"100644", b"a.py", b"a()\ndup()\nb()\ndup()\nc()\n")])
head = binary_commit(repo, [(b"100644", b"a.py", b"A()\ndup()\nB()\ndup()\nC()\n")])
result = SnapshotAdapter(repo).compare(comparison(base, head), declaration_paths=())
assert any("ambiguous_line_correspondence" in b for b in result.unresolved_boundaries)
assert not any(m.base_start_line in (2, 4) for m in result.line_correspondences)
def test_change_context_distinctive_line_anchors_despite_recurring_braces(repo: Path) -> None:
# '}' recurs across two runs (ambiguous), but the distinctive sink line is a single-run
# anchor and must still map -- this is the real "fix touches surrounding code" case.
base = binary_commit(repo, [(b"100644", b"a.js", b"f() {\n old();\n}\ng() {\n distinctive(x);\n}\n")])
head = binary_commit(repo, [(b"100644", b"a.js", b"f() {\n new();\n}\nh() {\n distinctive(x);\n}\n")])
result = SnapshotAdapter(repo).compare(comparison(base, head), declaration_paths=())
assert any("ambiguous_line_correspondence" in b for b in result.unresolved_boundaries)
assert any(m.base_start_line == 5 and m.head_start_line == 5 for m in result.line_correspondences)
@pytest.mark.parametrize("limit", ["max_total_bytes", "max_diff_bytes", "max_mapping_lines", "max_files"])
def test_change_context_total_limits_are_explicit(repo: Path, limit: str) -> None:
from openultrasast.push.snapshot import SnapshotLimits