FA-20936 / Assistive announcements / Open access
Completed speech promotes newest normal message first · case 01
The announcement trace violates the stated interrupt-resumption contract.
ROOT CAUSE
Fault site: elif waiting: current=waiting.pop(0) is implemented as elif waiting: current=waiting.pop()
VERIFIED REPAIR
Restore the stated transition: elif waiting: current=waiting.pop(0)
Unsuccessful approach: The attempted repair substitutes elif waiting: current=waiting.pop(0); current[2]=len(current[1]) and still violates a regression oracle.
Case contract
Speech jobs are [id,words,cursor]. Start queues a new job when one is active. Urgent pushes the current job on a LIFO suspended stack and begins fresh. Step speaks one word and finishes only after its last word; suspended jobs resume before queued normal work. Cancel ID removes it from every location and promotes next available job. Clear discards all jobs without speech. Report emitted [id,word] and current/suspended/waiting IDs with cursors.
Why this case matters
Deterministic controlled model of assistive announcement delivery.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
current=None; suspended=[]; waiting=[]; spoken=[]
for e in events:
if e[0] in ('start','urgent'):
job=[e[1],list(e[2]),0]
if e[0]=='urgent':
if current is not None: suspended.append(current)
current=job
elif current is None: current=job
else: waiting.append(job)
elif e[0]=='cancel':
if current is not None and current[0]==e[1]: current=None
suspended=[x for x in suspended if x[0]!=e[1]]
waiting=[x for x in waiting if x[0]!=e[1]]
elif e[0]=='clear': current=None; suspended=[]; waiting=[]
elif e[0]=='step' and current is not None:
if current[2]<len(current[1]):
spoken.append([current[0],current[1][current[2]]])
current[2]+=1
if current[2]>=len(current[1]): current=None
if current is None:
if suspended: current=suspended.pop()
elif waiting: current=waiting.pop()
return [spoken,None if current is None else [current[0],current[2]],[[x[0],x[2]] for x in suspended],[[x[0],x[2]] for x in waiting]]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = ["[['start','a',['a']*(N+2)],['step'],['urgent','b',['b']],['urgent','c',['c']],['step'],['step'],['step']]", "[['start','a',['a']*N],['start','b',['b']],['start','c',['c']]]+[['step']]*(N+2)", "[['start','a',['a']*N],['urgent','b',['b']],['start','c',['c']],['cancel','a'],['step'],['step']]", "[['start','a',['a']*N],['start','b',['beta']],['start','c',['gamma']],['cancel','b']]", "[['start','a',['a']*N],['urgent','b',['b']],['start','c',['c']],['clear']]", "[['start','a',['a']*N],['urgent','b',['b']],['urgent','c',['c']],['cancel','a']]", "[['start','a',[]],['step']]", "[['start','a',['a']*N],['cancel','a']]"]
expected = {1: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 2: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 3: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 4: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 5: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]]}[N]
for i, expression in enumerate(fixtures):
check("trace-"+str(i+1), solve(eval(expression)), expected[i])
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 |
|---|---|---|---|
| trace-1 | [[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []] | [[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []] | Passed |
| trace-2 | [[['a', 'a'], ['c', 'c'], ['b', 'b']], None, [], []] | [[['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []] | Failed |
| trace-3 | [[['b', 'b'], ['c', 'c']], None, [], []] | [[['b', 'b'], ['c', 'c']], None, [], []] | Passed |
| trace-4 | [[], ['a', 0], [], [['c', 0]]] | [[], ['a', 0], [], [['c', 0]]] | Passed |
| trace-5 | [[], None, [], []] | [[], None, [], []] | Passed |
| trace-6 | [[], ['c', 0], [['b', 0]], []] | [[], ['c', 0], [['b', 0]], []] | Passed |
| trace-7 | [[], None, [], []] | [[], None, [], []] | Passed |
| trace-8 | [[], None, [], []] | [[], None, [], []] | Passed |
SHA-256 / 758650347fb86b35c199ee9f64340d97ba19a421062c60f78f6d569364e90250
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
current=None; suspended=[]; waiting=[]; spoken=[]
for e in events:
if e[0] in ('start','urgent'):
job=[e[1],list(e[2]),0]
if e[0]=='urgent':
if current is not None: suspended.append(current)
current=job
elif current is None: current=job
else: waiting.append(job)
elif e[0]=='cancel':
if current is not None and current[0]==e[1]: current=None
suspended=[x for x in suspended if x[0]!=e[1]]
waiting=[x for x in waiting if x[0]!=e[1]]
elif e[0]=='clear': current=None; suspended=[]; waiting=[]
elif e[0]=='step' and current is not None:
if current[2]<len(current[1]):
spoken.append([current[0],current[1][current[2]]])
current[2]+=1
if current[2]>=len(current[1]): current=None
if current is None:
if suspended: current=suspended.pop()
elif waiting: current=waiting.pop(0); current[2]=len(current[1])
return [spoken,None if current is None else [current[0],current[2]],[[x[0],x[2]] for x in suspended],[[x[0],x[2]] for x in waiting]]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = ["[['start','a',['a']*(N+2)],['step'],['urgent','b',['b']],['urgent','c',['c']],['step'],['step'],['step']]", "[['start','a',['a']*N],['start','b',['b']],['start','c',['c']]]+[['step']]*(N+2)", "[['start','a',['a']*N],['urgent','b',['b']],['start','c',['c']],['cancel','a'],['step'],['step']]", "[['start','a',['a']*N],['start','b',['beta']],['start','c',['gamma']],['cancel','b']]", "[['start','a',['a']*N],['urgent','b',['b']],['start','c',['c']],['clear']]", "[['start','a',['a']*N],['urgent','b',['b']],['urgent','c',['c']],['cancel','a']]", "[['start','a',[]],['step']]", "[['start','a',['a']*N],['cancel','a']]"]
expected = {1: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 2: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 3: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 4: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 5: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]]}[N]
for i, expression in enumerate(fixtures):
check("trace-"+str(i+1), solve(eval(expression)), expected[i])
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 |
|---|---|---|---|
| trace-1 | [[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []] | [[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []] | Passed |
| trace-2 | [[['a', 'a']], None, [], []] | [[['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []] | Failed |
| trace-3 | [[['b', 'b']], None, [], []] | [[['b', 'b'], ['c', 'c']], None, [], []] | Failed |
| trace-4 | [[], ['a', 0], [], [['c', 0]]] | [[], ['a', 0], [], [['c', 0]]] | Passed |
| trace-5 | [[], None, [], []] | [[], None, [], []] | Passed |
| trace-6 | [[], ['c', 0], [['b', 0]], []] | [[], ['c', 0], [['b', 0]], []] | Passed |
| trace-7 | [[], None, [], []] | [[], None, [], []] | Passed |
| trace-8 | [[], None, [], []] | [[], None, [], []] | Passed |
SHA-256 / af43ab9e00be41b501ad1cb2a145c8072edfeeb5809e2d20346d72f35436706a
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(events):
current=None; suspended=[]; waiting=[]; spoken=[]
for e in events:
if e[0] in ('start','urgent'):
job=[e[1],list(e[2]),0]
if e[0]=='urgent':
if current is not None: suspended.append(current)
current=job
elif current is None: current=job
else: waiting.append(job)
elif e[0]=='cancel':
if current is not None and current[0]==e[1]: current=None
suspended=[x for x in suspended if x[0]!=e[1]]
waiting=[x for x in waiting if x[0]!=e[1]]
elif e[0]=='clear': current=None; suspended=[]; waiting=[]
elif e[0]=='step' and current is not None:
if current[2]<len(current[1]):
spoken.append([current[0],current[1][current[2]]])
current[2]+=1
if current[2]>=len(current[1]): current=None
if current is None:
if suspended: current=suspended.pop()
elif waiting: current=waiting.pop(0)
return [spoken,None if current is None else [current[0],current[2]],[[x[0],x[2]] for x in suspended],[[x[0],x[2]] for x in waiting]]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = ["[['start','a',['a']*(N+2)],['step'],['urgent','b',['b']],['urgent','c',['c']],['step'],['step'],['step']]", "[['start','a',['a']*N],['start','b',['b']],['start','c',['c']]]+[['step']]*(N+2)", "[['start','a',['a']*N],['urgent','b',['b']],['start','c',['c']],['cancel','a'],['step'],['step']]", "[['start','a',['a']*N],['start','b',['beta']],['start','c',['gamma']],['cancel','b']]", "[['start','a',['a']*N],['urgent','b',['b']],['start','c',['c']],['clear']]", "[['start','a',['a']*N],['urgent','b',['b']],['urgent','c',['c']],['cancel','a']]", "[['start','a',[]],['step']]", "[['start','a',['a']*N],['cancel','a']]"]
expected = {1: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 2: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 3: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 4: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]], 5: [[[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []], [[['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []], [[['b', 'b'], ['c', 'c']], None, [], []], [[], ['a', 0], [], [['c', 0]]], [[], None, [], []], [[], ['c', 0], [['b', 0]], []], [[], None, [], []], [[], None, [], []]]}[N]
for i, expression in enumerate(fixtures):
check("trace-"+str(i+1), solve(eval(expression)), expected[i])
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 |
|---|---|---|---|
| trace-1 | [[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []] | [[['a', 'a'], ['c', 'c'], ['b', 'b'], ['a', 'a']], ['a', 2], [], []] | Passed |
| trace-2 | [[['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []] | [[['a', 'a'], ['b', 'b'], ['c', 'c']], None, [], []] | Passed |
| trace-3 | [[['b', 'b'], ['c', 'c']], None, [], []] | [[['b', 'b'], ['c', 'c']], None, [], []] | Passed |
| trace-4 | [[], ['a', 0], [], [['c', 0]]] | [[], ['a', 0], [], [['c', 0]]] | Passed |
| trace-5 | [[], None, [], []] | [[], None, [], []] | Passed |
| trace-6 | [[], ['c', 0], [['b', 0]], []] | [[], ['c', 0], [['b', 0]], []] | Passed |
| trace-7 | [[], None, [], []] | [[], None, [], []] | Passed |
| trace-8 | [[], None, [], []] | [[], None, [], []] | Passed |
SHA-256 / 5e832a3e83061f1ee0093431e9d549659342050e125ab0deee199556dd419aae
Verification & scope
Stipulated bounded policy, not a browser, speech engine, platform API, or 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:40:23.825967+00:00.
Case digest / 5c78aab5a79b6556563d9ec042fa0d8466d684610475d7efaa24fc86bca37998