FA-54321 / Accessibility interaction semantics / Open access
Clearing a name does not emit a semantic update · case 01
Clearing a name does not emit a semantic update.
ROOT CAUSE
The empty name change decision in the snapshot delta adapter violates its declared contract.
VERIFIED REPAIR
Apply the exact contract at the empty name change decision: elif old['name'] != new['name']:
Unsuccessful approach: The local repair substitutes elif new['name'] != '' and old['name'] != new['name']: but still mishandles the regression fixtures.
Case contract
Input [before,after] maps stable object ID to {role,name,parent,order}. Return delta operations in phases: removed IDs sorted lexical; added IDs sorted by after order then ID; common IDs sorted lexical get role-replace if role differs, otherwise name-set if name differs; parent/order changes append move even after role-replace. Removed values use old role, added records preserve complete new record, name-set preserves empty names. Same ID is never remove+add just for role changes. Return operations lists.
Why this case matters
Offline accessibility bridge model with explicit policy; useful for testing semantic API adapters independently of browser implementations.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(data):
before,after = data
result = []
for ident in sorted(set(before)-set(after)): result.append(['remove',ident,before[ident]['role']])
for ident in sorted(set(after)-set(before),key=lambda x:(after[x]['order'],x)): result.append(['add',ident,after[ident]])
for ident in sorted(set(before)&set(after)):
old,new = before[ident],after[ident]
if old['role'] != new['role']: result.append(['replace-role',ident,new['role']])
elif new['name'] and old['name'] != new['name']: result.append(['set-name',ident,new['name']])
if old['parent'] != new['parent'] or old['order'] != new['order']: result.append(['move',ident,new['parent'],new['order']])
return result
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('contract fixture 1', solve([{'x':{'role':'image','name':'old','parent':'a','order':N}},{'x':{'role':'button','name':'new','parent':'b','order':N+1}}],), [['replace-role','x','button'],['move','x','b',N+1]])
check('contract fixture 2', solve([{'x':{'role':'image','name':'old','parent':None,'order':0}},{'x':{'role':'image','name':'','parent':None,'order':0}}],), [['set-name','x','']])
check('contract fixture 3', solve([{'z':{'role':'image','name':'Z','parent':None,'order':0},'a':{'role':'group','name':'A','parent':None,'order':1}},{}],), [['remove','a','group'],['remove','z','image']])
check('contract fixture 4', solve([{}, {'z':{'role':'image','name':'Z','parent':None,'order':0},'a':{'role':'group','name':'A','parent':None,'order':1}}],), [['add','z',{'role':'image','name':'Z','parent':None,'order':0}],['add','a',{'role':'group','name':'A','parent':None,'order':1}]])
check('contract fixture 5', solve([{'x':{'role':'image','name':'X','parent':'a','order':0}},{'x':{'role':'image','name':'X','parent':'a','order':N}}],), [['move','x','a',N]])
check('contract fixture 6', solve([{'x':{'role':'image','name':'X','parent':'a','order':0}},{'x':{'role':'image','name':'X','parent':'b','order':0}}],), [['move','x','b',0]])
check('contract fixture 7', solve([{},{}],), [])
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 |
|---|---|---|---|
| contract fixture 1 | [['replace-role', 'x', 'button'], ['move', 'x', 'b', 2]] | [['replace-role', 'x', 'button'], ['move', 'x', 'b', 2]] | Passed |
| contract fixture 2 | [] | [['set-name', 'x', '']] | Failed |
| contract fixture 3 | [['remove', 'a', 'group'], ['remove', 'z', 'image']] | [['remove', 'a', 'group'], ['remove', 'z', 'image']] | Passed |
| contract fixture 4 | [['add', 'z', {'name': 'Z', 'order': 0, 'parent': None, 'role': 'image'}], ['add', 'a', {'name': 'A', 'order': 1, 'parent': None, 'role': 'group'}]] | [['add', 'z', {'name': 'Z', 'order': 0, 'parent': None, 'role': 'image'}], ['add', 'a', {'name': 'A', 'order': 1, 'parent': None, 'role': 'group'}]] | Passed |
| contract fixture 5 | [['move', 'x', 'a', 1]] | [['move', 'x', 'a', 1]] | Passed |
| contract fixture 6 | [['move', 'x', 'b', 0]] | [['move', 'x', 'b', 0]] | Passed |
| contract fixture 7 | [] | [] | Passed |
SHA-256 / 9c3ab65beac97710f9f497c59948d3e58116de356321bde502aaf54fb0d6c419
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(data):
before,after = data
result = []
for ident in sorted(set(before)-set(after)): result.append(['remove',ident,before[ident]['role']])
for ident in sorted(set(after)-set(before),key=lambda x:(after[x]['order'],x)): result.append(['add',ident,after[ident]])
for ident in sorted(set(before)&set(after)):
old,new = before[ident],after[ident]
if old['role'] != new['role']: result.append(['replace-role',ident,new['role']])
elif new['name'] != '' and old['name'] != new['name']: result.append(['set-name',ident,new['name']])
if old['parent'] != new['parent'] or old['order'] != new['order']: result.append(['move',ident,new['parent'],new['order']])
return result
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('contract fixture 1', solve([{'x':{'role':'image','name':'old','parent':'a','order':N}},{'x':{'role':'button','name':'new','parent':'b','order':N+1}}],), [['replace-role','x','button'],['move','x','b',N+1]])
check('contract fixture 2', solve([{'x':{'role':'image','name':'old','parent':None,'order':0}},{'x':{'role':'image','name':'','parent':None,'order':0}}],), [['set-name','x','']])
check('contract fixture 3', solve([{'z':{'role':'image','name':'Z','parent':None,'order':0},'a':{'role':'group','name':'A','parent':None,'order':1}},{}],), [['remove','a','group'],['remove','z','image']])
check('contract fixture 4', solve([{}, {'z':{'role':'image','name':'Z','parent':None,'order':0},'a':{'role':'group','name':'A','parent':None,'order':1}}],), [['add','z',{'role':'image','name':'Z','parent':None,'order':0}],['add','a',{'role':'group','name':'A','parent':None,'order':1}]])
check('contract fixture 5', solve([{'x':{'role':'image','name':'X','parent':'a','order':0}},{'x':{'role':'image','name':'X','parent':'a','order':N}}],), [['move','x','a',N]])
check('contract fixture 6', solve([{'x':{'role':'image','name':'X','parent':'a','order':0}},{'x':{'role':'image','name':'X','parent':'b','order':0}}],), [['move','x','b',0]])
check('contract fixture 7', solve([{},{}],), [])
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 |
|---|---|---|---|
| contract fixture 1 | [['replace-role', 'x', 'button'], ['move', 'x', 'b', 2]] | [['replace-role', 'x', 'button'], ['move', 'x', 'b', 2]] | Passed |
| contract fixture 2 | [] | [['set-name', 'x', '']] | Failed |
| contract fixture 3 | [['remove', 'a', 'group'], ['remove', 'z', 'image']] | [['remove', 'a', 'group'], ['remove', 'z', 'image']] | Passed |
| contract fixture 4 | [['add', 'z', {'name': 'Z', 'order': 0, 'parent': None, 'role': 'image'}], ['add', 'a', {'name': 'A', 'order': 1, 'parent': None, 'role': 'group'}]] | [['add', 'z', {'name': 'Z', 'order': 0, 'parent': None, 'role': 'image'}], ['add', 'a', {'name': 'A', 'order': 1, 'parent': None, 'role': 'group'}]] | Passed |
| contract fixture 5 | [['move', 'x', 'a', 1]] | [['move', 'x', 'a', 1]] | Passed |
| contract fixture 6 | [['move', 'x', 'b', 0]] | [['move', 'x', 'b', 0]] | Passed |
| contract fixture 7 | [] | [] | Passed |
SHA-256 / 1c68c3ad237e42dd18b5aeedb141decb6d381e661367c1a770ea33c2f78cb748
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(data):
before,after = data
result = []
for ident in sorted(set(before)-set(after)): result.append(['remove',ident,before[ident]['role']])
for ident in sorted(set(after)-set(before),key=lambda x:(after[x]['order'],x)): result.append(['add',ident,after[ident]])
for ident in sorted(set(before)&set(after)):
old,new = before[ident],after[ident]
if old['role'] != new['role']: result.append(['replace-role',ident,new['role']])
elif old['name'] != new['name']: result.append(['set-name',ident,new['name']])
if old['parent'] != new['parent'] or old['order'] != new['order']: result.append(['move',ident,new['parent'],new['order']])
return result
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('contract fixture 1', solve([{'x':{'role':'image','name':'old','parent':'a','order':N}},{'x':{'role':'button','name':'new','parent':'b','order':N+1}}],), [['replace-role','x','button'],['move','x','b',N+1]])
check('contract fixture 2', solve([{'x':{'role':'image','name':'old','parent':None,'order':0}},{'x':{'role':'image','name':'','parent':None,'order':0}}],), [['set-name','x','']])
check('contract fixture 3', solve([{'z':{'role':'image','name':'Z','parent':None,'order':0},'a':{'role':'group','name':'A','parent':None,'order':1}},{}],), [['remove','a','group'],['remove','z','image']])
check('contract fixture 4', solve([{}, {'z':{'role':'image','name':'Z','parent':None,'order':0},'a':{'role':'group','name':'A','parent':None,'order':1}}],), [['add','z',{'role':'image','name':'Z','parent':None,'order':0}],['add','a',{'role':'group','name':'A','parent':None,'order':1}]])
check('contract fixture 5', solve([{'x':{'role':'image','name':'X','parent':'a','order':0}},{'x':{'role':'image','name':'X','parent':'a','order':N}}],), [['move','x','a',N]])
check('contract fixture 6', solve([{'x':{'role':'image','name':'X','parent':'a','order':0}},{'x':{'role':'image','name':'X','parent':'b','order':0}}],), [['move','x','b',0]])
check('contract fixture 7', solve([{},{}],), [])
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 |
|---|---|---|---|
| contract fixture 1 | [['replace-role', 'x', 'button'], ['move', 'x', 'b', 2]] | [['replace-role', 'x', 'button'], ['move', 'x', 'b', 2]] | Passed |
| contract fixture 2 | [['set-name', 'x', '']] | [['set-name', 'x', '']] | Passed |
| contract fixture 3 | [['remove', 'a', 'group'], ['remove', 'z', 'image']] | [['remove', 'a', 'group'], ['remove', 'z', 'image']] | Passed |
| contract fixture 4 | [['add', 'z', {'name': 'Z', 'order': 0, 'parent': None, 'role': 'image'}], ['add', 'a', {'name': 'A', 'order': 1, 'parent': None, 'role': 'group'}]] | [['add', 'z', {'name': 'Z', 'order': 0, 'parent': None, 'role': 'image'}], ['add', 'a', {'name': 'A', 'order': 1, 'parent': None, 'role': 'group'}]] | Passed |
| contract fixture 5 | [['move', 'x', 'a', 1]] | [['move', 'x', 'a', 1]] | Passed |
| contract fixture 6 | [['move', 'x', 'b', 0]] | [['move', 'x', 'b', 0]] | Passed |
| contract fixture 7 | [] | [] | Passed |
SHA-256 / 9354be8a55177f12917ccdb77fbde6bed5abc6e05a83b2081b5ca1d202d92629
Verification & scope
Stipulated finite adapter contract, not a claim of platform accessibility or ARIA conformance. 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:45:47.236503+00:00.
Case digest / 440cb4e1896af58f1d45bf52fa4061812e837258425fcb77faaf393c4cba45f0