FA-30036 / HTTP retries / Open access
Idempotency receipt expiry during retry: New expiry violates retry transition semantics · case 01
New expiry violates retry transition semantics
ROOT CAUSE
New expiry violates retry transition semantics
VERIFIED REPAIR
Restore the specified transition if expires<=now:.
Unsuccessful approach: The attempted repair changes the faulty site to if expires<0: but still violates a regression oracle.
Case contract
put(key,body,expires) creates a pending receipt if no live receipt exists at current logical time. Existing live matching key returns pending or cached; mismatched body returns conflict. commit succeeds only for a live pending receipt. at(time) moves monotonic time forward. Expiry at now is expired. lookup(key) reports missing/pending/cached. Return outcomes. Commit appends committed or ignored to expose whether a durable receipt write occurred.
Why this case matters
Offline deterministic model of HTTP request retries.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
now=0; records={}; out=[]
for e in events:
if e[0]=='at': now=max(now,e[1])
elif e[0]=='put':
k,body,expires=e[1:]
old=records.get(k)
if old is not None and old[1]<=now:
records.pop(k); old=None
if old is None:
if expires<now: out.append('expired'); continue
records[k]=[body,expires,False,None]; out.append('new')
elif old[0]!=body: out.append('conflict')
elif old[2]: out.append(['cached',old[3]])
else: out.append('pending')
elif e[0]=='commit':
old=records.get(e[1])
if old is not None and old[1]>now and not old[2]:
old[2]=True; old[3]=e[2]; out.append('committed')
else: out.append('ignored')
elif e[0]=='lookup':
old=records.get(e[1])
if old is None or old[1]<=now: out.append('missing')
elif old[2]: out.append(['cached',old[3]])
else: out.append('pending')
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [])
check('1', solve([('lookup','x'),('put','a',N,3),('put','a',N,3),('commit','a',N+9),('commit','a',999),('lookup','a'),('put','a',N,3),('put','a',N+1,3)]), ['missing','new','pending','committed','ignored',['cached',N+9],['cached',N+9],'conflict'])
check('2', solve([('at',3),('put','a',N,3),('put','b',N,2)]), ['expired','expired'])
check('3', solve([('put','a',N,3),('at',3),('commit','a',99),('lookup','a'),('put','a',N+1,5),('lookup','a')]), ['new','ignored','missing','new','pending'])
check('4', solve([('at',4),('put','a',N,6),('at',6),('lookup','a')]), ['new','missing'])
check('5', solve([('put','a',N,2),('at',3),('at',1),('lookup','a')]), ['new','missing'])
check('6', solve([('commit','absent',N),('lookup','absent')]), ['ignored','missing'])
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 0 | [] | [] | Passed |
| 1 | ['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict'] | ['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict'] | Passed |
| 2 | ['new', 'expired'] | ['expired', 'expired'] | Failed |
| 3 | ['new', 'ignored', 'missing', 'new', 'pending'] | ['new', 'ignored', 'missing', 'new', 'pending'] | Passed |
| 4 | ['new', 'missing'] | ['new', 'missing'] | Passed |
| 5 | ['new', 'missing'] | ['new', 'missing'] | Passed |
| 6 | ['ignored', 'missing'] | ['ignored', 'missing'] | Passed |
SHA-256 / 7400ec1bfb316a39e9f0e8efe6f6f1f6267d97f190ddcba252736c6ce47da904
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
now=0; records={}; out=[]
for e in events:
if e[0]=='at': now=max(now,e[1])
elif e[0]=='put':
k,body,expires=e[1:]
old=records.get(k)
if old is not None and old[1]<=now:
records.pop(k); old=None
if old is None:
if expires<0: out.append('expired'); continue
records[k]=[body,expires,False,None]; out.append('new')
elif old[0]!=body: out.append('conflict')
elif old[2]: out.append(['cached',old[3]])
else: out.append('pending')
elif e[0]=='commit':
old=records.get(e[1])
if old is not None and old[1]>now and not old[2]:
old[2]=True; old[3]=e[2]; out.append('committed')
else: out.append('ignored')
elif e[0]=='lookup':
old=records.get(e[1])
if old is None or old[1]<=now: out.append('missing')
elif old[2]: out.append(['cached',old[3]])
else: out.append('pending')
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [])
check('1', solve([('lookup','x'),('put','a',N,3),('put','a',N,3),('commit','a',N+9),('commit','a',999),('lookup','a'),('put','a',N,3),('put','a',N+1,3)]), ['missing','new','pending','committed','ignored',['cached',N+9],['cached',N+9],'conflict'])
check('2', solve([('at',3),('put','a',N,3),('put','b',N,2)]), ['expired','expired'])
check('3', solve([('put','a',N,3),('at',3),('commit','a',99),('lookup','a'),('put','a',N+1,5),('lookup','a')]), ['new','ignored','missing','new','pending'])
check('4', solve([('at',4),('put','a',N,6),('at',6),('lookup','a')]), ['new','missing'])
check('5', solve([('put','a',N,2),('at',3),('at',1),('lookup','a')]), ['new','missing'])
check('6', solve([('commit','absent',N),('lookup','absent')]), ['ignored','missing'])
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 0 | [] | [] | Passed |
| 1 | ['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict'] | ['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict'] | Passed |
| 2 | ['new', 'new'] | ['expired', 'expired'] | Failed |
| 3 | ['new', 'ignored', 'missing', 'new', 'pending'] | ['new', 'ignored', 'missing', 'new', 'pending'] | Passed |
| 4 | ['new', 'missing'] | ['new', 'missing'] | Passed |
| 5 | ['new', 'missing'] | ['new', 'missing'] | Passed |
| 6 | ['ignored', 'missing'] | ['ignored', 'missing'] | Passed |
SHA-256 / 8fe1d7557ce0343936908b08b1bbae504a1f5bcc76a7066e589d90a3eced2fd6
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
now=0; records={}; out=[]
for e in events:
if e[0]=='at': now=max(now,e[1])
elif e[0]=='put':
k,body,expires=e[1:]
old=records.get(k)
if old is not None and old[1]<=now:
records.pop(k); old=None
if old is None:
if expires<=now: out.append('expired'); continue
records[k]=[body,expires,False,None]; out.append('new')
elif old[0]!=body: out.append('conflict')
elif old[2]: out.append(['cached',old[3]])
else: out.append('pending')
elif e[0]=='commit':
old=records.get(e[1])
if old is not None and old[1]>now and not old[2]:
old[2]=True; old[3]=e[2]; out.append('committed')
else: out.append('ignored')
elif e[0]=='lookup':
old=records.get(e[1])
if old is None or old[1]<=now: out.append('missing')
elif old[2]: out.append(['cached',old[3]])
else: out.append('pending')
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [])
check('1', solve([('lookup','x'),('put','a',N,3),('put','a',N,3),('commit','a',N+9),('commit','a',999),('lookup','a'),('put','a',N,3),('put','a',N+1,3)]), ['missing','new','pending','committed','ignored',['cached',N+9],['cached',N+9],'conflict'])
check('2', solve([('at',3),('put','a',N,3),('put','b',N,2)]), ['expired','expired'])
check('3', solve([('put','a',N,3),('at',3),('commit','a',99),('lookup','a'),('put','a',N+1,5),('lookup','a')]), ['new','ignored','missing','new','pending'])
check('4', solve([('at',4),('put','a',N,6),('at',6),('lookup','a')]), ['new','missing'])
check('5', solve([('put','a',N,2),('at',3),('at',1),('lookup','a')]), ['new','missing'])
check('6', solve([('commit','absent',N),('lookup','absent')]), ['ignored','missing'])
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 0 | [] | [] | Passed |
| 1 | ['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict'] | ['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict'] | Passed |
| 2 | ['expired', 'expired'] | ['expired', 'expired'] | Passed |
| 3 | ['new', 'ignored', 'missing', 'new', 'pending'] | ['new', 'ignored', 'missing', 'new', 'pending'] | Passed |
| 4 | ['new', 'missing'] | ['new', 'missing'] | Passed |
| 5 | ['new', 'missing'] | ['new', 'missing'] | Passed |
| 6 | ['ignored', 'missing'] | ['ignored', 'missing'] | Passed |
SHA-256 / aba0cac156c38bf6144172a6872debb1334951fa4c0faca744649e01ee90e8bb
Verification & scope
Stipulated bounded simulator, not a complete HTTP implementation or a standards conformance claim. 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:41:48.959720+00:00.
Case digest / f342d6cf8f151acdc7c8aceeefb9ac4cb7e21980567be62efa929e2b8b145a2b