FAILURE MAP
← Case archive

FA-076 / Distributed coordination / Open access

Read repair resurrects a deleted record · case 01

A surviving old value wins because the merge removes deletion markers before comparing versions.

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

ROOT CAUSE

A tombstone is treated as absence rather than a versioned state transition.

VERIFIED REPAIR

Choose the greatest version across values and tombstones, preferring deletion on an equal-version conflict.

Unsuccessful approach: Making any tombstone permanently dominant also discards legitimate later recreations.

Case contract

Records are [version,kind,value] with kind put or delete. Return the latest value or None when the latest record is a deletion; deletion wins an equal-version put/delete tie. Equal-version put values are assumed identical; the model has no wall-clock conflict resolution.

Why this case matters

Models anti-entropy repair between replicas retaining different parts of a delete/recreate history.

1 / The failure

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

N = 1
observations = []
def solve(records):
    live = [r for r in records if r[1] == 'put']
    return max(live, key=lambda r: r[0])[2] if live else None
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('new delete suppresses stale put', solve([[N, 'put', 'old'], [N+1, 'delete', None]]), None)
check('later recreation', solve([[N, 'delete', None], [N+1, 'put', 'new']]), 'new')
check('delete wins version tie', solve([[N, 'put', 'old'], [N, 'delete', None]]), None)
check('tie independent of arrival', solve([[N, 'delete', None], [N, 'put', 'old']]), None)
check('newer of two live values', solve([[N, 'put', 'old'], [N+2, 'put', 'new']]), 'new')
check('empty replicas', solve([]), None)
check('only tombstone', solve([[N, 'delete', None]]), None)
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
new delete suppresses stale putoldNoneFailed
later recreationnewnewPassed
delete wins version tieoldNoneFailed
tie independent of arrivaloldNoneFailed
newer of two live valuesnewnewPassed
empty replicasNoneNonePassed
only tombstoneNoneNonePassed

SHA-256 / 6215124347f360e6afeccddaf115baa03285ad3db7d4fcf6ef24e7f7942fd92f

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(records):
    if any(r[1] == 'delete' for r in records):
        return None
    return max(records, key=lambda r: r[0])[2] if records else None
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('new delete suppresses stale put', solve([[N, 'put', 'old'], [N+1, 'delete', None]]), None)
check('later recreation', solve([[N, 'delete', None], [N+1, 'put', 'new']]), 'new')
check('delete wins version tie', solve([[N, 'put', 'old'], [N, 'delete', None]]), None)
check('tie independent of arrival', solve([[N, 'delete', None], [N, 'put', 'old']]), None)
check('newer of two live values', solve([[N, 'put', 'old'], [N+2, 'put', 'new']]), 'new')
check('empty replicas', solve([]), None)
check('only tombstone', solve([[N, 'delete', None]]), None)
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
new delete suppresses stale putNoneNonePassed
later recreationNonenewFailed
delete wins version tieNoneNonePassed
tie independent of arrivalNoneNonePassed
newer of two live valuesnewnewPassed
empty replicasNoneNonePassed
only tombstoneNoneNonePassed

SHA-256 / be9b211bb506d50d0a12b0ec0fb16a67c618e364511d8828d34fc7adade5f5c9

3 / The verified repair

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

N = 1
observations = []
def solve(records):
    if not records:
        return None
    latest = max(records, key=lambda r: (r[0], r[1] == 'delete'))
    return latest[2] if latest[1] == 'put' else None
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('new delete suppresses stale put', solve([[N, 'put', 'old'], [N+1, 'delete', None]]), None)
check('later recreation', solve([[N, 'delete', None], [N+1, 'put', 'new']]), 'new')
check('delete wins version tie', solve([[N, 'put', 'old'], [N, 'delete', None]]), None)
check('tie independent of arrival', solve([[N, 'delete', None], [N, 'put', 'old']]), None)
check('newer of two live values', solve([[N, 'put', 'old'], [N+2, 'put', 'new']]), 'new')
check('empty replicas', solve([]), None)
check('only tombstone', solve([[N, 'delete', None]]), None)
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
new delete suppresses stale putNoneNonePassed
later recreationnewnewPassed
delete wins version tieNoneNonePassed
tie independent of arrivalNoneNonePassed
newer of two live valuesnewnewPassed
empty replicasNoneNonePassed
only tombstoneNoneNonePassed

SHA-256 / f077d7ac121357388304a537375a2226fc373e93513953894cc7605cdc117674

Verification & scope

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

Case digest / 88aa78c61a37e369d20aa160fa77c631a86852117a06892198877921662b79ef