FA-7871 / Keyboard accessibility / Open access
Modal focus restoration: Restoration targets a detached opener · case 01
Restoration targets a detached opener.
ROOT CAUSE
The connected operation uses `if True` where the contract requires `if x in connected`.
VERIFIED REPAIR
Implement the connected operation as `if x in connected`.
Unsuccessful approach: Selecting detached nodes still cannot restore focus.
Case contract
Opening pushes one return target; closing or Escape pops one scope; restoration chooses the newest connected target; clear empties the stack.
Why this case matters
A deterministic model of modal focus restoration; this isolates one interface invariant without requiring a browser.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(stack, event, target, connected):
stack = list(stack)
if event == 'open': return stack + [target]
if event == 'close': return stack[:-1]
if event == 'restore': return next((x for x in reversed(stack) if True), None)
if event == 'escape': return stack[:-1] if stack else []
if event == 'clear': return []
return stack
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('nested open', solve(['outer'], 'open', 'inner', []), ['outer', 'inner'])
check('nested close', solve(['outer','inner'], 'close', None, []), ['outer'])
check('newest live', solve(['z','a'], 'restore', None, ['z','a']), 'a')
check('ordered newest', solve(['a','z'], 'restore', None, ['a','z']), 'z')
check('detached', solve(['outer','gone'], 'restore', None, ['outer']), 'outer')
check('escape one', solve(['outer','inner'], 'escape', None, []), ['outer'])
check('clear', solve(['outer'], 'clear', None, []), [])
check('parameterized opener', solve(['outer'], 'open', 'dialog'+str(N), []), ['outer', 'dialog'+str(N)])
for repetition in range(N):
check('repeat clear', solve(['outer'], 'clear', 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| nested open | ['outer', 'inner'] | ['outer', 'inner'] | Passed |
| nested close | ['outer'] | ['outer'] | Passed |
| newest live | a | a | Passed |
| ordered newest | z | z | Passed |
| detached | gone | outer | Failed |
| escape one | ['outer'] | ['outer'] | Passed |
| clear | [] | [] | Passed |
| parameterized opener | ['outer', 'dialog1'] | ['outer', 'dialog1'] | Passed |
| repeat clear | [] | [] | Passed |
SHA-256 / 490068627e857f7bb90fdd5f07aa7c9a57c0d77ae6372ebb62b41d9d96a7fd7e
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(stack, event, target, connected):
stack = list(stack)
if event == 'open': return stack + [target]
if event == 'close': return stack[:-1]
if event == 'restore': return next((x for x in reversed(stack) if x not in connected), None)
if event == 'escape': return stack[:-1] if stack else []
if event == 'clear': return []
return stack
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('nested open', solve(['outer'], 'open', 'inner', []), ['outer', 'inner'])
check('nested close', solve(['outer','inner'], 'close', None, []), ['outer'])
check('newest live', solve(['z','a'], 'restore', None, ['z','a']), 'a')
check('ordered newest', solve(['a','z'], 'restore', None, ['a','z']), 'z')
check('detached', solve(['outer','gone'], 'restore', None, ['outer']), 'outer')
check('escape one', solve(['outer','inner'], 'escape', None, []), ['outer'])
check('clear', solve(['outer'], 'clear', None, []), [])
check('parameterized opener', solve(['outer'], 'open', 'dialog'+str(N), []), ['outer', 'dialog'+str(N)])
for repetition in range(N):
check('repeat clear', solve(['outer'], 'clear', 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| nested open | ['outer', 'inner'] | ['outer', 'inner'] | Passed |
| nested close | ['outer'] | ['outer'] | Passed |
| newest live | None | a | Failed |
| ordered newest | None | z | Failed |
| detached | gone | outer | Failed |
| escape one | ['outer'] | ['outer'] | Passed |
| clear | [] | [] | Passed |
| parameterized opener | ['outer', 'dialog1'] | ['outer', 'dialog1'] | Passed |
| repeat clear | [] | [] | Passed |
SHA-256 / 1696518443c17ecd5314bbe11b96496fda47f3d81657f8c481ffb64fc74f4e3a
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(stack, event, target, connected):
stack = list(stack)
if event == 'open': return stack + [target]
if event == 'close': return stack[:-1]
if event == 'restore': return next((x for x in reversed(stack) if x in connected), None)
if event == 'escape': return stack[:-1] if stack else []
if event == 'clear': return []
return stack
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('nested open', solve(['outer'], 'open', 'inner', []), ['outer', 'inner'])
check('nested close', solve(['outer','inner'], 'close', None, []), ['outer'])
check('newest live', solve(['z','a'], 'restore', None, ['z','a']), 'a')
check('ordered newest', solve(['a','z'], 'restore', None, ['a','z']), 'z')
check('detached', solve(['outer','gone'], 'restore', None, ['outer']), 'outer')
check('escape one', solve(['outer','inner'], 'escape', None, []), ['outer'])
check('clear', solve(['outer'], 'clear', None, []), [])
check('parameterized opener', solve(['outer'], 'open', 'dialog'+str(N), []), ['outer', 'dialog'+str(N)])
for repetition in range(N):
check('repeat clear', solve(['outer'], 'clear', 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| nested open | ['outer', 'inner'] | ['outer', 'inner'] | Passed |
| nested close | ['outer'] | ['outer'] | Passed |
| newest live | a | a | Passed |
| ordered newest | z | z | Passed |
| detached | outer | outer | Passed |
| escape one | ['outer'] | ['outer'] | Passed |
| clear | [] | [] | Passed |
| parameterized opener | ['outer', 'dialog1'] | ['outer', 'dialog1'] | Passed |
| repeat clear | [] | [] | Passed |
SHA-256 / 8901a8b8c65cfc32a871a684954e66a19f6e67cb2db84f1b471e3d704b7c9981
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:16.127421+00:00.
Case digest / bc755ce0f2483b214c3e62ec40fa944a9bdf38c7799c855027eedf0d6ff8fc63