FAILURE MAP
← Case archive

FA-21576 / Assistive announcements / Open access

Base-language match beats an exact speech locale · case 01

The announcement trace violates the stated voice-negotiation contract.

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

ROOT CAUSE

Fault site: language_rank=0 if v[1]==lang else (1 if v[1].split('-')[0]==lang.split('-')[0] else 2) is implemented as language_rank=1 if v[1]==lang else (0 if v[1].split('-')[0]==lang.split('-')[0] else 2)

VERIFIED REPAIR

Restore the stated transition: language_rank=0 if v[1]==lang else (1 if v[1].split('-')[0]==lang.split('-')[0] else 2)

Unsuccessful approach: The attempted repair substitutes language_rank=0 if v[1].split('-')[0]==lang.split('-')[0] else 2 and still violates a regression oracle.

Case contract

Voice inventory rows are [id,language,local,quality,features]. Request [language,offline,required-features,preferred-id]. Selection excludes unavailable or missing-feature voices, honors exact preferred only among eligible, then ranks exact language before base-language before en fallback, local before remote, higher quality, then lexical ID. Add replaces same ID; remove marks unavailable; enable restores it. Return selected IDs or None, inventory IDs and disabled IDs.

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):
    voices={}; disabled=set(); out=[]
    for e in events:
        if e[0]=='add': voices[e[1]]=e[1:]
        elif e[0]=='remove': disabled.add(e[1])
        elif e[0]=='enable': disabled.discard(e[1])
        elif e[0]=='request':
            lang,offline,required,preferred=e[1:]
            rows=[v for k,v in voices.items() if k not in disabled]
            rows=[v for v in rows if not offline or v[2]]
            rows=[v for v in rows if set(required)<=set(v[4])]
            rows=[v for v in rows if v[1]==lang or v[1].split('-')[0]==lang.split('-')[0] or v[1]=='en']
            if not rows: out.append(None); continue
            def rank(v):
                language_rank=1 if v[1]==lang else (0 if v[1].split('-')[0]==lang.split('-')[0] else 2)
                return (v[0]!=preferred,language_rank,not v[2],-v[3],v[0])
            out.append(min(rows,key=rank)[0])
    return [out,list(voices),sorted(disabled)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = ["[['add','a','en',True,1,['marks']],['add','b','en',True,N+5,['marks','spell']],['request','en',True,['marks'],None]]", "[['add','a','fr',False,9,['marks']],['add','b','fr',True,1,['marks']],['request','fr',False,[],None],['request','fr',True,[],None],['request','fr',False,[],'a']]", "[['add','z','fr-CA',True,1,[]],['add','a','fr',True,9,[]],['add','e','en',True,1,[]],['request','fr-CA',False,[],None],['request','fr-BE',False,[],None],['request','fr-CA',False,[],'e']]", "[['add','a','en',True,9,['marks']],['add','b','en',True,2,['marks','spell','pause']],['request','en',True,['marks','spell'],None],['add','a','en',True,10,['marks','spell']],['request','en',True,['spell'],None]]", "[['add','a','en',True,N+5,[]],['add','b','en',True,1,[]],['remove','a'],['request','en',False,[],None],['remove','b'],['enable','b'],['request','en',False,[],None]]", "[['add','remote','ja',False,N,[]],['request','ja',False,[],None],['request','ja',True,[],None],['request','de',False,[],'remote']]", "[['request','en',True,[],None]]"]
expected = {1: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 2: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 3: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 4: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 5: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[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 fixtureActualExpectedOutcome
trace-1[['b'], ['a', 'b'], []][['b'], ['a', 'b'], []]Passed
trace-2[['b', 'b', 'a'], ['a', 'b'], []][['b', 'b', 'a'], ['a', 'b'], []]Passed
trace-3[['a', 'a', 'e'], ['z', 'a', 'e'], []][['z', 'a', 'e'], ['z', 'a', 'e'], []]Failed
trace-4[['b', 'a'], ['a', 'b'], []][['b', 'a'], ['a', 'b'], []]Passed
trace-5[['b', 'b'], ['a', 'b'], ['a']][['b', 'b'], ['a', 'b'], ['a']]Passed
trace-6[['remote', None, None], ['remote'], []][['remote', None, None], ['remote'], []]Passed
trace-7[[None], [], []][[None], [], []]Passed

SHA-256 / 41cdc3a8e4d9c0d5ebc0fae9785a9bdd66e67e5db2e09db3d9a6edd6b7eaf125

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(events):
    voices={}; disabled=set(); out=[]
    for e in events:
        if e[0]=='add': voices[e[1]]=e[1:]
        elif e[0]=='remove': disabled.add(e[1])
        elif e[0]=='enable': disabled.discard(e[1])
        elif e[0]=='request':
            lang,offline,required,preferred=e[1:]
            rows=[v for k,v in voices.items() if k not in disabled]
            rows=[v for v in rows if not offline or v[2]]
            rows=[v for v in rows if set(required)<=set(v[4])]
            rows=[v for v in rows if v[1]==lang or v[1].split('-')[0]==lang.split('-')[0] or v[1]=='en']
            if not rows: out.append(None); continue
            def rank(v):
                language_rank=0 if v[1].split('-')[0]==lang.split('-')[0] else 2
                return (v[0]!=preferred,language_rank,not v[2],-v[3],v[0])
            out.append(min(rows,key=rank)[0])
    return [out,list(voices),sorted(disabled)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = ["[['add','a','en',True,1,['marks']],['add','b','en',True,N+5,['marks','spell']],['request','en',True,['marks'],None]]", "[['add','a','fr',False,9,['marks']],['add','b','fr',True,1,['marks']],['request','fr',False,[],None],['request','fr',True,[],None],['request','fr',False,[],'a']]", "[['add','z','fr-CA',True,1,[]],['add','a','fr',True,9,[]],['add','e','en',True,1,[]],['request','fr-CA',False,[],None],['request','fr-BE',False,[],None],['request','fr-CA',False,[],'e']]", "[['add','a','en',True,9,['marks']],['add','b','en',True,2,['marks','spell','pause']],['request','en',True,['marks','spell'],None],['add','a','en',True,10,['marks','spell']],['request','en',True,['spell'],None]]", "[['add','a','en',True,N+5,[]],['add','b','en',True,1,[]],['remove','a'],['request','en',False,[],None],['remove','b'],['enable','b'],['request','en',False,[],None]]", "[['add','remote','ja',False,N,[]],['request','ja',False,[],None],['request','ja',True,[],None],['request','de',False,[],'remote']]", "[['request','en',True,[],None]]"]
expected = {1: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 2: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 3: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 4: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 5: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[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 fixtureActualExpectedOutcome
trace-1[['b'], ['a', 'b'], []][['b'], ['a', 'b'], []]Passed
trace-2[['b', 'b', 'a'], ['a', 'b'], []][['b', 'b', 'a'], ['a', 'b'], []]Passed
trace-3[['a', 'a', 'e'], ['z', 'a', 'e'], []][['z', 'a', 'e'], ['z', 'a', 'e'], []]Failed
trace-4[['b', 'a'], ['a', 'b'], []][['b', 'a'], ['a', 'b'], []]Passed
trace-5[['b', 'b'], ['a', 'b'], ['a']][['b', 'b'], ['a', 'b'], ['a']]Passed
trace-6[['remote', None, None], ['remote'], []][['remote', None, None], ['remote'], []]Passed
trace-7[[None], [], []][[None], [], []]Passed

SHA-256 / 4048cfc8e52e7df757828f44dbba02ab83bb3cfd648b814e20801fb78d5cf4e0

3 / The verified repair

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

N = 1
observations = []
def solve(events):
    voices={}; disabled=set(); out=[]
    for e in events:
        if e[0]=='add': voices[e[1]]=e[1:]
        elif e[0]=='remove': disabled.add(e[1])
        elif e[0]=='enable': disabled.discard(e[1])
        elif e[0]=='request':
            lang,offline,required,preferred=e[1:]
            rows=[v for k,v in voices.items() if k not in disabled]
            rows=[v for v in rows if not offline or v[2]]
            rows=[v for v in rows if set(required)<=set(v[4])]
            rows=[v for v in rows if v[1]==lang or v[1].split('-')[0]==lang.split('-')[0] or v[1]=='en']
            if not rows: out.append(None); continue
            def rank(v):
                language_rank=0 if v[1]==lang else (1 if v[1].split('-')[0]==lang.split('-')[0] else 2)
                return (v[0]!=preferred,language_rank,not v[2],-v[3],v[0])
            out.append(min(rows,key=rank)[0])
    return [out,list(voices),sorted(disabled)]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = ["[['add','a','en',True,1,['marks']],['add','b','en',True,N+5,['marks','spell']],['request','en',True,['marks'],None]]", "[['add','a','fr',False,9,['marks']],['add','b','fr',True,1,['marks']],['request','fr',False,[],None],['request','fr',True,[],None],['request','fr',False,[],'a']]", "[['add','z','fr-CA',True,1,[]],['add','a','fr',True,9,[]],['add','e','en',True,1,[]],['request','fr-CA',False,[],None],['request','fr-BE',False,[],None],['request','fr-CA',False,[],'e']]", "[['add','a','en',True,9,['marks']],['add','b','en',True,2,['marks','spell','pause']],['request','en',True,['marks','spell'],None],['add','a','en',True,10,['marks','spell']],['request','en',True,['spell'],None]]", "[['add','a','en',True,N+5,[]],['add','b','en',True,1,[]],['remove','a'],['request','en',False,[],None],['remove','b'],['enable','b'],['request','en',False,[],None]]", "[['add','remote','ja',False,N,[]],['request','ja',False,[],None],['request','ja',True,[],None],['request','de',False,[],'remote']]", "[['request','en',True,[],None]]"]
expected = {1: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 2: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 3: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 4: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[None], [], []]], 5: [[['b'], ['a', 'b'], []], [['b', 'b', 'a'], ['a', 'b'], []], [['z', 'a', 'e'], ['z', 'a', 'e'], []], [['b', 'a'], ['a', 'b'], []], [['b', 'b'], ['a', 'b'], ['a']], [['remote', None, None], ['remote'], []], [[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 fixtureActualExpectedOutcome
trace-1[['b'], ['a', 'b'], []][['b'], ['a', 'b'], []]Passed
trace-2[['b', 'b', 'a'], ['a', 'b'], []][['b', 'b', 'a'], ['a', 'b'], []]Passed
trace-3[['z', 'a', 'e'], ['z', 'a', 'e'], []][['z', 'a', 'e'], ['z', 'a', 'e'], []]Passed
trace-4[['b', 'a'], ['a', 'b'], []][['b', 'a'], ['a', 'b'], []]Passed
trace-5[['b', 'b'], ['a', 'b'], ['a']][['b', 'b'], ['a', 'b'], ['a']]Passed
trace-6[['remote', None, None], ['remote'], []][['remote', None, None], ['remote'], []]Passed
trace-7[[None], [], []][[None], [], []]Passed

SHA-256 / 106057a0e719a31ce229386466b4b3f9f445e738eecdb6b0f6987d30a031c506

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

Case digest / 660a64b9cc64ec1dd4654460eb508e58a015e31c2393fec19176d6cbead1de8c