FAILURE MAP
← Case archive

FA-75731 / Text diff and three-way merge / Open access

Blame propagation: removed lines do not advance the old-line cursor · case 01

Lines after a deletion are credited to the commits of the deleted lines.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The loop over removed lines never moves the cursor into the old annotations.

VERIFIED REPAIR

Advance the old-line cursor once for every removed line.

Unsuccessful approach: Advancing once per run still misattributes lines after a multi-line deletion.

Case contract

Given the old file's per-line commit annotations and an edit script, produce annotations for the new file: context lines keep their old annotation, added lines get the new commit. With ignore_ws, within a run of removed lines followed by added lines, the i-th added line keeps the i-th removed line's annotation when both are equal after deleting all whitespace.

Why this case matters

Blame and annotate views follow each line through history; whitespace-insensitive blame should not credit reformatting commits.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(ann, ops, commit, ignore_ws):
    res = []
    i = 0
    k = 0
    while k < len(ops):
        tag = ops[k][0]
        if tag == ' ':
            res.append(ann[i])
            i += 1
            k += 1
            continue
        dels = []
        while k < len(ops) and ops[k][0] == '-':
            dels.append((ann[i], ops[k][1]))
            k += 1
        adds = []
        while k < len(ops) and ops[k][0] == '+':
            adds.append(ops[k][1])
            k += 1
        for idx, text in enumerate(adds):
            if ignore_ws and idx < len(dels) and ''.join(dels[idx][1].split()) == ''.join(text.split()):
                res.append(dels[idx][0])
            else:
                res.append(commit)
    return res
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = {
    1: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N1', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    2: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N2', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    3: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N3', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    4: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N4', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    5: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N5', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
}[N]
for label, args, expected in cases:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
context keeps attribution['c1', 'c2']['c1', 'c3']Failed
insertion attributed to the new commit['c1', 'N', 'c2']['c1', 'N', 'c2']Passed
removed lines advance the old cursor['c1', 'c2']['c3', 'c4']Failed
whitespace-only change keeps blame when ignoring whitespace['c1', 'c1']['c1', 'c2']Failed
whitespace-only change is new without the flag['N', 'c1']['N', 'c2']Failed
second pair compared positionally['c1', 'c1', 'c1']['c1', 'c2', 'c3']Failed
extra additions are new['c1', 'N', 'c1']['c1', 'N', 'c2']Failed
real change within a whitespace block['c1', 'N', 'c1']['c1', 'N', 'c3']Failed

SHA-256 / 22535da568a606ee63f1a462b8f95384a82716351ae3a17b83d31c2e395aaf42

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(ann, ops, commit, ignore_ws):
    res = []
    i = 0
    k = 0
    while k < len(ops):
        tag = ops[k][0]
        if tag == ' ':
            res.append(ann[i])
            i += 1
            k += 1
            continue
        dels = []
        while k < len(ops) and ops[k][0] == '-':
            dels.append((ann[i], ops[k][1]))
            k += 1
        i += 1 if dels else 0
        adds = []
        while k < len(ops) and ops[k][0] == '+':
            adds.append(ops[k][1])
            k += 1
        for idx, text in enumerate(adds):
            if ignore_ws and idx < len(dels) and ''.join(dels[idx][1].split()) == ''.join(text.split()):
                res.append(dels[idx][0])
            else:
                res.append(commit)
    return res
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = {
    1: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N1', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    2: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N2', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    3: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N3', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    4: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N4', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    5: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N5', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
}[N]
for label, args, expected in cases:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
context keeps attribution['c1', 'c3']['c1', 'c3']Passed
insertion attributed to the new commit['c1', 'N', 'c2']['c1', 'N', 'c2']Passed
removed lines advance the old cursor['c2', 'c3']['c3', 'c4']Failed
whitespace-only change keeps blame when ignoring whitespace['c1', 'c2']['c1', 'c2']Passed
whitespace-only change is new without the flag['N', 'c2']['N', 'c2']Passed
second pair compared positionally['c1', 'c1', 'c2']['c1', 'c2', 'c3']Failed
extra additions are new['c1', 'N', 'c2']['c1', 'N', 'c2']Passed
real change within a whitespace block['c1', 'N', 'c2']['c1', 'N', 'c3']Failed

SHA-256 / c256a52c2aa3c256267b8e5072c9365a0526d01174326e5aaa9ccad461eacb04

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(ann, ops, commit, ignore_ws):
    res = []
    i = 0
    k = 0
    while k < len(ops):
        tag = ops[k][0]
        if tag == ' ':
            res.append(ann[i])
            i += 1
            k += 1
            continue
        dels = []
        while k < len(ops) and ops[k][0] == '-':
            dels.append((ann[i], ops[k][1]))
            i += 1
            k += 1
        adds = []
        while k < len(ops) and ops[k][0] == '+':
            adds.append(ops[k][1])
            k += 1
        for idx, text in enumerate(adds):
            if ignore_ws and idx < len(dels) and ''.join(dels[idx][1].split()) == ''.join(text.split()):
                res.append(dels[idx][0])
            else:
                res.append(commit)
    return res
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = {
    1: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N1', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    2: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N2', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    3: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N3', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    4: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N4', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
    5: [('context keeps attribution', [['c1', 'c2', 'c3'], [[' ', 'a'], ['-', 'b'], [' ', 'c']], 'N', False], ['c1', 'c3']), ('insertion attributed to the new commit', [['c1', 'c2'], [[' ', 'a'], ['+', 'x'], [' ', 'b']], 'N', False], ['c1', 'N', 'c2']), ('removed lines advance the old cursor', [['c1', 'c2', 'c3', 'c4'], [['-', 'a'], ['-', 'b'], [' ', 'c'], [' ', 'd']], 'N', True], ['c3', 'c4']), ('whitespace-only change keeps blame when ignoring whitespace', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', True], ['c1', 'c2']), ('whitespace-only change is new without the flag', [['c1', 'c2'], [['-', 'f(a,b)'], ['+', 'f(a, b)'], [' ', 'z']], 'N', False], ['N', 'c2']), ('second pair compared positionally', [['c1', 'c2', 'c3'], [['-', 'x = 1'], ['-', 'y = 2'], ['+', 'x=1'], ['+', 'y =  2'], [' ', 'k']], 'N5', True], ['c1', 'c2', 'c3']), ('extra additions are new', [['c1', 'c2'], [['-', 'p'], ['+', 'p '], ['+', 'p'], [' ', 'q']], 'N', True], ['c1', 'N', 'c2']), ('real change within a whitespace block', [['c1', 'c2', 'c3'], [['-', 'u'], ['-', 'v'], ['+', 'u'], ['+', 'w'], [' ', 't']], 'N', True], ['c1', 'N', 'c3'])],
}[N]
for label, args, expected in cases:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
context keeps attribution['c1', 'c3']['c1', 'c3']Passed
insertion attributed to the new commit['c1', 'N', 'c2']['c1', 'N', 'c2']Passed
removed lines advance the old cursor['c3', 'c4']['c3', 'c4']Passed
whitespace-only change keeps blame when ignoring whitespace['c1', 'c2']['c1', 'c2']Passed
whitespace-only change is new without the flag['N', 'c2']['N', 'c2']Passed
second pair compared positionally['c1', 'c2', 'c3']['c1', 'c2', 'c3']Passed
extra additions are new['c1', 'N', 'c2']['c1', 'N', 'c2']Passed
real change within a whitespace block['c1', 'N', 'c3']['c1', 'N', 'c3']Passed

SHA-256 / d4528568653c52e70162c7d70fe2e90bde1762740af533d308824a624b901b3f

Verification & scope

A deterministic, bounded teaching model of one diff, patch or merge rule with stipulated conventions; it is not a production diff or version-control implementation and makes no claim of conformance to any specific tool. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:49:09.623432+00:00.

Case digest / 4dec42f76f176159d02c4b63abb9a914091465fc76a552660e3e09777a7bde15