FAILURE MAP
← Case archive

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

Blame propagation: whitespace-only rewrites keep old blame even when not requested · case 01

Plain blame hides reformatting commits.

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

ROOT CAUSE

The whitespace-insensitive pairing runs regardless of the ignore_ws flag.

VERIFIED REPAIR

Keep old annotations for whitespace-only rewrites only when ignore_ws is set.

Unsuccessful approach: Comparing stripped lines ignores only leading and trailing whitespace, missing reformatting inside the line.

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]))
            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 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['c1', 'c2']['N', 'c2']Failed
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 / 47fb8bcc6230323a9458dae1195b52da6a14d6ab7fef41c566f07ee8f4d4a27b

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]))
            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 dels[idx][1].strip() == text.strip():
                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['N', 'c2']['c1', 'c2']Failed
whitespace-only change is new without the flag['N', 'c2']['N', 'c2']Passed
second pair compared positionally['N1', 'N1', 'c3']['c1', 'c2', 'c3']Failed
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 / 01ccac287880206fe7b954c36b50a17fd9a268c5e30e29219e353a41cbd10f72

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.663408+00:00.

Case digest / 3263645ca6017aeca472314b5278911d71ad2d8eabf003e58d23725a6f54fd13