FAILURE MAP
← Case archive

FA-11411 / Accessibility interaction semantics / Open access

Table headers leak across row groups or override explicit associations · case 01

A cell announces a group header belonging to a different section or inferred headers alongside explicit ones.

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

ROOT CAUSE

Header inference ignores the cell’s group identity and explicit-association boundary.

VERIFIED REPAIR

When explicit IDs are present resolve only them; otherwise associate by row, column, or exact group scope.

Unsuccessful approach: Matching groups repairs inferred associations but still merges inferred headers into an explicit header list.

Case contract

Controlled table model: each header is [id,scope,index,group], scope is row, col, rowgroup or colgroup. Cell is [row,col,rowgroup,colgroup]. None explicit IDs enables inference: row/col match index, group scopes match corresponding group. An explicit list, even empty, disables inference; resolve its existing IDs in given order, deduplicating. Return IDs; fixture IDs are unique and every header has one scope.

Why this case matters

A controlled offline accessibility-data model. It isolates the stated contract; it does not simulate browser accessibility APIs or claim full ARIA conformance.

1 / The failure

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

N = 1
observations = []
def solve(headers, cell, explicit):
    return [ident for ident,scope,index,group in headers if scope in ('rowgroup','colgroup') or (scope == 'row' and index == cell[0]) or (scope == 'col' and index == cell[1])]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('row group boundary', solve([('old','rowgroup',0,'old'),('current','rowgroup',0,str(N))], [N,0,str(N),'c'], None), ['current'])
check('column group boundary', solve([('old','colgroup',0,'old'),('current','colgroup',0,str(N))], [0,N,'r',str(N)], None), ['current'])
check('explicit overrides inference', solve([('inferred','row',N,'r'),('chosen','col',100,'c')], [N,0,'r','c'], ['chosen']), ['chosen'])
check('empty explicit suppresses inference', solve([('row','row',N,'r')], [N,0,'r','c'], []), [])
check('explicit order and dedup', solve([('a','row',N,'r'),('b','col',N,'c')], [N,N,'r','c'], ['b','gone','a','b']), ['b','a'])
check('row and column inference', solve([('row','row',N,'r'),('col','col',N+1,'c'),('other','row',N+1,'r')], [N,N+1,'r','c'], None), ['row','col'])
check('empty table', solve([], [N,N,'r','c'], 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
row group boundary['old', 'current']['current']Failed
column group boundary['old', 'current']['current']Failed
explicit overrides inference['inferred']['chosen']Failed
empty explicit suppresses inference['row'][]Failed
explicit order and dedup['a', 'b']['b', 'a']Failed
row and column inference['row', 'col']['row', 'col']Passed
empty table[][]Passed

SHA-256 / 29651f45eaee63b3efd780cb496ff6fb8e3fb7bc5029cbcf92c6c0af22cdd763

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(headers, cell, explicit):
    inferred = [ident for ident,scope,index,group in headers if (scope == 'row' and index == cell[0]) or (scope == 'col' and index == cell[1]) or (scope == 'rowgroup' and group == cell[2]) or (scope == 'colgroup' and group == cell[3])]
    known = {h[0] for h in headers}
    return list(dict.fromkeys(([x for x in explicit if x in known] if explicit is not None else []) + inferred))
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('row group boundary', solve([('old','rowgroup',0,'old'),('current','rowgroup',0,str(N))], [N,0,str(N),'c'], None), ['current'])
check('column group boundary', solve([('old','colgroup',0,'old'),('current','colgroup',0,str(N))], [0,N,'r',str(N)], None), ['current'])
check('explicit overrides inference', solve([('inferred','row',N,'r'),('chosen','col',100,'c')], [N,0,'r','c'], ['chosen']), ['chosen'])
check('empty explicit suppresses inference', solve([('row','row',N,'r')], [N,0,'r','c'], []), [])
check('explicit order and dedup', solve([('a','row',N,'r'),('b','col',N,'c')], [N,N,'r','c'], ['b','gone','a','b']), ['b','a'])
check('row and column inference', solve([('row','row',N,'r'),('col','col',N+1,'c'),('other','row',N+1,'r')], [N,N+1,'r','c'], None), ['row','col'])
check('empty table', solve([], [N,N,'r','c'], 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
row group boundary['current']['current']Passed
column group boundary['current']['current']Passed
explicit overrides inference['chosen', 'inferred']['chosen']Failed
empty explicit suppresses inference['row'][]Failed
explicit order and dedup['b', 'a']['b', 'a']Passed
row and column inference['row', 'col']['row', 'col']Passed
empty table[][]Passed

SHA-256 / 26dc338b8d8a43ff9e772c9d6f39220f21e87d3ba57f3874d1928d46799e5c64

3 / The verified repair

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

N = 1
observations = []
def solve(headers, cell, explicit):
    known = {h[0] for h in headers}
    if explicit is not None: return list(dict.fromkeys(x for x in explicit if x in known))
    return [ident for ident,scope,index,group in headers if (scope == 'row' and index == cell[0]) or (scope == 'col' and index == cell[1]) or (scope == 'rowgroup' and group == cell[2]) or (scope == 'colgroup' and group == cell[3])]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('row group boundary', solve([('old','rowgroup',0,'old'),('current','rowgroup',0,str(N))], [N,0,str(N),'c'], None), ['current'])
check('column group boundary', solve([('old','colgroup',0,'old'),('current','colgroup',0,str(N))], [0,N,'r',str(N)], None), ['current'])
check('explicit overrides inference', solve([('inferred','row',N,'r'),('chosen','col',100,'c')], [N,0,'r','c'], ['chosen']), ['chosen'])
check('empty explicit suppresses inference', solve([('row','row',N,'r')], [N,0,'r','c'], []), [])
check('explicit order and dedup', solve([('a','row',N,'r'),('b','col',N,'c')], [N,N,'r','c'], ['b','gone','a','b']), ['b','a'])
check('row and column inference', solve([('row','row',N,'r'),('col','col',N+1,'c'),('other','row',N+1,'r')], [N,N+1,'r','c'], None), ['row','col'])
check('empty table', solve([], [N,N,'r','c'], 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
row group boundary['current']['current']Passed
column group boundary['current']['current']Passed
explicit overrides inference['chosen']['chosen']Passed
empty explicit suppresses inference[][]Passed
explicit order and dedup['b', 'a']['b', 'a']Passed
row and column inference['row', 'col']['row', 'col']Passed
empty table[][]Passed

SHA-256 / f98788bfecb65c5c64931527d3bea9c957155119d787da832fbeb1cc1dda18ce

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

Case digest / 37ff8bc28a44a8637545e6efd41965d58e2c47374933bb865791e6b599551dc9