FAILURE MAP
← Case archive

FA-7861 / Keyboard accessibility / Open access

Modal focus restoration: Closing a nested dialog discards the outer scope · case 01

Closing a nested dialog discards the outer scope.

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

ROOT CAUSE

The close operation uses `if event == 'close': return []` where the contract requires `if event == 'close': return stack[:-1]`.

VERIFIED REPAIR

Implement the close operation as `if event == 'close': return stack[:-1]`.

Unsuccessful approach: Dropping the oldest entry retains the closing scope.

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 []
    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 fixtureActualExpectedOutcome
nested open['outer', 'inner']['outer', 'inner']Passed
nested close[]['outer']Failed
newest liveaaPassed
ordered newestzzPassed
detachedouterouterPassed
escape one['outer']['outer']Passed
clear[][]Passed
parameterized opener['outer', 'dialog1']['outer', 'dialog1']Passed
repeat clear[][]Passed

SHA-256 / f9ed3243c62188c363d3f7da8403f2a035e3d3aa521611f4124ff17489728ee1

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 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 fixtureActualExpectedOutcome
nested open['outer', 'inner']['outer', 'inner']Passed
nested close['inner']['outer']Failed
newest liveaaPassed
ordered newestzzPassed
detachedouterouterPassed
escape one['outer']['outer']Passed
clear[][]Passed
parameterized opener['outer', 'dialog1']['outer', 'dialog1']Passed
repeat clear[][]Passed

SHA-256 / e670af1bb35e01e49d65af75ed3208bb05f47a5a2f75a6626e4200dababf1994

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 fixtureActualExpectedOutcome
nested open['outer', 'inner']['outer', 'inner']Passed
nested close['outer']['outer']Passed
newest liveaaPassed
ordered newestzzPassed
detachedouterouterPassed
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:15.996511+00:00.

Case digest / b70ed7314faf7429af75c85d9143137c9ccda82af6089089088e38c981d3100f