FAILURE MAP
← Case archive

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

Three-way key-value merge: deleting a key and setting it to null are the same state · case 01

Setting a value to null is merged as a deletion, and a deleted key can come back as null.

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

ROOT CAUSE

Lookups default to None, which collides with explicit null values.

VERIFIED REPAIR

Use a private sentinel for absent keys so null remains an ordinary value.

Unsuccessful approach: Using the sentinel only for the base still conflates null and absence on the two sides.

Case contract

Merge base, ours and theirs maps key by key over the union of keys; an absent key and a key holding null are different states. If ours and theirs agree, take that state; else if ours equals base take theirs; else if theirs equals base take ours; otherwise record a conflict and keep ours. Return sorted merged [key, value] pairs and sorted conflicting keys.

Why this case matters

Structured configuration and lock files are merged per key, where deletions and null values must stay distinguishable.

1 / The failure

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

N = 1
observations = []
def solve(base, ours, theirs):
    MISSING = object()
    merged, conflicts = {}, []
    for k in sorted(set(base) | set(ours) | set(theirs)):
        b = base.get(k)
        o = ours.get(k)
        t = theirs.get(k)
        if o == t:
            r = o
        elif o == b:
            r = t
        elif t == b:
            r = o
        else:
            conflicts.append(k)
            r = o
        if r is not MISSING:
            merged[k] = r
    return {'merged': sorted([k, v] for k, v in merged.items()), 'conflicts': conflicts}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = {
    1: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '81', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z1'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e1'}], {'merged': [['extra', 'e1'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm1'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    2: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '82', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '82']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z2'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z2'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e2'}], {'merged': [['extra', 'e2'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm2'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm2'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    3: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '83', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '83']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z3'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z3'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e3'}], {'merged': [['extra', 'e3'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm3'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm3'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    4: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '84', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '84']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z4'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z4'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e4'}], {'merged': [['extra', 'e4'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm4'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm4'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    5: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '85', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '85']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z5'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z5'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e5'}], {'merged': [['extra', 'e5'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm5'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm5'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
}[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
each side changes a different key{'conflicts': [], 'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']]}{'conflicts': [], 'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']]}Passed
both change a key the same way{'conflicts': [], 'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']]}Passed
both delete the same key{'conflicts': [], 'merged': [['host', 'a'], ['mode', None], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['port', '80']]}Failed
conflicting changes keep ours{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']]}{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']]}Passed
delete versus modify conflicts{'conflicts': ['mode'], 'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']]}{'conflicts': ['mode'], 'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']]}Passed
explicit null differs from deletion{'conflicts': [], 'merged': [['host', 'a'], ['mode', None], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mode', None], ['port', '80']]}Passed
key added only by theirs{'conflicts': [], 'merged': [['extra', 'e1'], ['host', 'a'], ['mode', 'x'], ['port', '80']]}{'conflicts': [], 'merged': [['extra', 'e1'], ['host', 'a'], ['mode', 'x'], ['port', '80']]}Passed
key added only by ours{'conflicts': [], 'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']]}Passed
modify versus delete conflicts{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x'], ['port', None]]}{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x']]}Failed
null in base and deleted by ours{'conflicts': [], 'merged': [['j', '2'], ['k', None]]}{'conflicts': [], 'merged': [['j', '2']]}Failed

SHA-256 / d636406f4eb64ec657ce3dd341e3e0e1ba6926c56f30f3551aa8ab6727fd9263

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(base, ours, theirs):
    MISSING = object()
    merged, conflicts = {}, []
    for k in sorted(set(base) | set(ours) | set(theirs)):
        b = base.get(k, MISSING)
        o = ours.get(k)
        t = theirs.get(k)
        if o == t:
            r = o
        elif o == b:
            r = t
        elif t == b:
            r = o
        else:
            conflicts.append(k)
            r = o
        if r is not MISSING:
            merged[k] = r
    return {'merged': sorted([k, v] for k, v in merged.items()), 'conflicts': conflicts}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = {
    1: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '81', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z1'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e1'}], {'merged': [['extra', 'e1'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm1'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    2: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '82', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '82']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z2'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z2'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e2'}], {'merged': [['extra', 'e2'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm2'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm2'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    3: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '83', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '83']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z3'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z3'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e3'}], {'merged': [['extra', 'e3'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm3'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm3'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    4: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '84', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '84']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z4'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z4'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e4'}], {'merged': [['extra', 'e4'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm4'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm4'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    5: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '85', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '85']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z5'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z5'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e5'}], {'merged': [['extra', 'e5'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm5'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm5'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
}[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
each side changes a different key{'conflicts': [], 'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']]}{'conflicts': [], 'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']]}Passed
both change a key the same way{'conflicts': [], 'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']]}Passed
both delete the same key{'conflicts': [], 'merged': [['host', 'a'], ['mode', None], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['port', '80']]}Failed
conflicting changes keep ours{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']]}{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']]}Passed
delete versus modify conflicts{'conflicts': ['mode'], 'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']]}{'conflicts': ['mode'], 'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']]}Passed
explicit null differs from deletion{'conflicts': [], 'merged': [['host', 'a'], ['mode', None], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mode', None], ['port', '80']]}Passed
key added only by theirs{'conflicts': ['extra'], 'merged': [['extra', None], ['host', 'a'], ['mode', 'x'], ['port', '80']]}{'conflicts': [], 'merged': [['extra', 'e1'], ['host', 'a'], ['mode', 'x'], ['port', '80']]}Failed
key added only by ours{'conflicts': ['mine'], 'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']]}Failed
modify versus delete conflicts{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x'], ['port', None]]}{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x']]}Failed
null in base and deleted by ours{'conflicts': [], 'merged': [['j', '2'], ['k', None]]}{'conflicts': [], 'merged': [['j', '2']]}Failed

SHA-256 / b66fb96aaf79d77945a621777be16e942018bfde2aeee39af58417edb5eb083b

3 / The verified repair

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

N = 1
observations = []
def solve(base, ours, theirs):
    MISSING = object()
    merged, conflicts = {}, []
    for k in sorted(set(base) | set(ours) | set(theirs)):
        b = base.get(k, MISSING)
        o = ours.get(k, MISSING)
        t = theirs.get(k, MISSING)
        if o == t:
            r = o
        elif o == b:
            r = t
        elif t == b:
            r = o
        else:
            conflicts.append(k)
            r = o
        if r is not MISSING:
            merged[k] = r
    return {'merged': sorted([k, v] for k, v in merged.items()), 'conflicts': conflicts}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = {
    1: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '81', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z1'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e1'}], {'merged': [['extra', 'e1'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm1'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    2: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '82', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '82']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z2'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z2'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e2'}], {'merged': [['extra', 'e2'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm2'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm2'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    3: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '83', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '83']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z3'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z3'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e3'}], {'merged': [['extra', 'e3'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm3'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm3'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    4: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '84', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '84']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z4'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z4'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e4'}], {'merged': [['extra', 'e4'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm4'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm4'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
    5: [('each side changes a different key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'b', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '85', 'mode': 'x'}], {'merged': [['host', 'b'], ['mode', 'x'], ['port', '85']], 'conflicts': []}), ('both change a key the same way', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'y'}, {'host': 'a', 'port': '80', 'mode': 'y'}], {'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']], 'conflicts': []}), ('both delete the same key', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['port', '80']], 'conflicts': []}), ('conflicting changes keep ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '1', 'mode': 'x'}, {'host': 'a', 'port': '2', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']], 'conflicts': ['port']}), ('delete versus modify conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'z5'}, {'host': 'a', 'port': '80'}], {'merged': [['host', 'a'], ['mode', 'z5'], ['port', '80']], 'conflicts': ['mode']}), ('explicit null differs from deletion', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': None}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', None], ['port', '80']], 'conflicts': []}), ('key added only by theirs', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'extra': 'e5'}], {'merged': [['extra', 'e5'], ['host', 'a'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('key added only by ours', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'port': '80', 'mode': 'x', 'mine': 'm5'}, {'host': 'a', 'port': '80', 'mode': 'x'}], {'merged': [['host', 'a'], ['mine', 'm5'], ['mode', 'x'], ['port', '80']], 'conflicts': []}), ('modify versus delete conflicts', [{'host': 'a', 'port': '80', 'mode': 'x'}, {'host': 'a', 'mode': 'x'}, {'host': 'a', 'port': '443', 'mode': 'x'}], {'merged': [['host', 'a'], ['mode', 'x']], 'conflicts': ['port']}), ('null in base and deleted by ours', [{'k': None, 'j': '1'}, {'j': '1'}, {'k': None, 'j': '2'}], {'merged': [['j', '2']], 'conflicts': []})],
}[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
each side changes a different key{'conflicts': [], 'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']]}{'conflicts': [], 'merged': [['host', 'b'], ['mode', 'x'], ['port', '81']]}Passed
both change a key the same way{'conflicts': [], 'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mode', 'y'], ['port', '80']]}Passed
both delete the same key{'conflicts': [], 'merged': [['host', 'a'], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['port', '80']]}Passed
conflicting changes keep ours{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']]}{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x'], ['port', '1']]}Passed
delete versus modify conflicts{'conflicts': ['mode'], 'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']]}{'conflicts': ['mode'], 'merged': [['host', 'a'], ['mode', 'z1'], ['port', '80']]}Passed
explicit null differs from deletion{'conflicts': [], 'merged': [['host', 'a'], ['mode', None], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mode', None], ['port', '80']]}Passed
key added only by theirs{'conflicts': [], 'merged': [['extra', 'e1'], ['host', 'a'], ['mode', 'x'], ['port', '80']]}{'conflicts': [], 'merged': [['extra', 'e1'], ['host', 'a'], ['mode', 'x'], ['port', '80']]}Passed
key added only by ours{'conflicts': [], 'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']]}{'conflicts': [], 'merged': [['host', 'a'], ['mine', 'm1'], ['mode', 'x'], ['port', '80']]}Passed
modify versus delete conflicts{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x']]}{'conflicts': ['port'], 'merged': [['host', 'a'], ['mode', 'x']]}Passed
null in base and deleted by ours{'conflicts': [], 'merged': [['j', '2']]}{'conflicts': [], 'merged': [['j', '2']]}Passed

SHA-256 / 8d90e3af1313999b155a9ede11ce8e2147e1a6712b46c8e934df371b31dd4764

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

Case digest / 622189bd46396097a08bcbdbf0e1f50c8e84e247f22922b9310f5186ee6edd75