FAILURE MAP
← Case archive

FA-8186 / Overlay interactions / Open access

Popover outside-interaction handling: Escape fails to dismiss the overlay · case 01

Escape fails to dismiss the overlay.

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

ROOT CAUSE

The escape operation uses `if event == 'escape': return opened` where the contract requires `if event == 'escape': return False`.

VERIFIED REPAIR

Implement the escape operation as `if event == 'escape': return False`.

Unsuccessful approach: Toggling Escape opens an overlay when it is already closed.

Case contract

Cancelled events preserve state; Escape closes; only pointerdown toggles triggers or dismisses outside; membership in the composed path recognizes descendants and portals.

Why this case matters

A deterministic model of popover outside-interaction handling; 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(opened, target_path, trigger, panel, event, prevented):
    if prevented: return opened
    if event == 'escape': return opened
    if event != 'pointerdown': return opened
    if trigger in target_path: return not opened
    if panel in target_path: return opened
    return False
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('cancelled open', solve(True, ['outside'], 'trigger', 'panel', 'pointerdown', True), True)
check('cancelled closed', solve(False, ['outside'], 'trigger', 'panel', 'pointerdown', True), False)
check('escape open', solve(True, [], 'trigger', 'panel', 'escape', False), False)
check('escape closed', solve(False, [], 'trigger', 'panel', 'escape', False), False)
check('ignored', solve(True, [], 'trigger', 'panel', 'pointermove', False), True)
check('trigger opens', solve(False, ['icon','trigger'], 'trigger', 'panel', 'pointerdown', False), True)
check('trigger closes', solve(True, ['icon','trigger'], 'trigger', 'panel', 'pointerdown', False), False)
check('inside descendant', solve(True, ['button','panel'], 'trigger', 'panel', 'pointerdown', False), True)
check('parameterized descendant depth', solve(True, ['node'+str(i) for i in range(N)]+['panel'], 'trigger', 'panel', 'pointerdown', False), True)
for repetition in range(N):
    check('repeat inside descendant', solve(True, ['button','panel'], 'trigger', 'panel', 'pointerdown', False), True)
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
cancelled openTrueTruePassed
cancelled closedFalseFalsePassed
escape openTrueFalseFailed
escape closedFalseFalsePassed
ignoredTrueTruePassed
trigger opensTrueTruePassed
trigger closesFalseFalsePassed
inside descendantTrueTruePassed
parameterized descendant depthTrueTruePassed
repeat inside descendantTrueTruePassed

SHA-256 / 5e76bbb1d16b90bda6500877877227e14ed120ae7b776bb54895eb1e4c8b3b94

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(opened, target_path, trigger, panel, event, prevented):
    if prevented: return opened
    if event == 'escape': return not opened
    if event != 'pointerdown': return opened
    if trigger in target_path: return not opened
    if panel in target_path: return opened
    return False
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('cancelled open', solve(True, ['outside'], 'trigger', 'panel', 'pointerdown', True), True)
check('cancelled closed', solve(False, ['outside'], 'trigger', 'panel', 'pointerdown', True), False)
check('escape open', solve(True, [], 'trigger', 'panel', 'escape', False), False)
check('escape closed', solve(False, [], 'trigger', 'panel', 'escape', False), False)
check('ignored', solve(True, [], 'trigger', 'panel', 'pointermove', False), True)
check('trigger opens', solve(False, ['icon','trigger'], 'trigger', 'panel', 'pointerdown', False), True)
check('trigger closes', solve(True, ['icon','trigger'], 'trigger', 'panel', 'pointerdown', False), False)
check('inside descendant', solve(True, ['button','panel'], 'trigger', 'panel', 'pointerdown', False), True)
check('parameterized descendant depth', solve(True, ['node'+str(i) for i in range(N)]+['panel'], 'trigger', 'panel', 'pointerdown', False), True)
for repetition in range(N):
    check('repeat inside descendant', solve(True, ['button','panel'], 'trigger', 'panel', 'pointerdown', False), True)
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
cancelled openTrueTruePassed
cancelled closedFalseFalsePassed
escape openFalseFalsePassed
escape closedTrueFalseFailed
ignoredTrueTruePassed
trigger opensTrueTruePassed
trigger closesFalseFalsePassed
inside descendantTrueTruePassed
parameterized descendant depthTrueTruePassed
repeat inside descendantTrueTruePassed

SHA-256 / 3ee9acf3c6f55503e6d1527702f67b5ca13836c1871ba2d92011a91525501fc4

3 / The verified repair

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

N = 1
observations = []
def solve(opened, target_path, trigger, panel, event, prevented):
    if prevented: return opened
    if event == 'escape': return False
    if event != 'pointerdown': return opened
    if trigger in target_path: return not opened
    if panel in target_path: return opened
    return False
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('cancelled open', solve(True, ['outside'], 'trigger', 'panel', 'pointerdown', True), True)
check('cancelled closed', solve(False, ['outside'], 'trigger', 'panel', 'pointerdown', True), False)
check('escape open', solve(True, [], 'trigger', 'panel', 'escape', False), False)
check('escape closed', solve(False, [], 'trigger', 'panel', 'escape', False), False)
check('ignored', solve(True, [], 'trigger', 'panel', 'pointermove', False), True)
check('trigger opens', solve(False, ['icon','trigger'], 'trigger', 'panel', 'pointerdown', False), True)
check('trigger closes', solve(True, ['icon','trigger'], 'trigger', 'panel', 'pointerdown', False), False)
check('inside descendant', solve(True, ['button','panel'], 'trigger', 'panel', 'pointerdown', False), True)
check('parameterized descendant depth', solve(True, ['node'+str(i) for i in range(N)]+['panel'], 'trigger', 'panel', 'pointerdown', False), True)
for repetition in range(N):
    check('repeat inside descendant', solve(True, ['button','panel'], 'trigger', 'panel', 'pointerdown', False), True)
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
cancelled openTrueTruePassed
cancelled closedFalseFalsePassed
escape openFalseFalsePassed
escape closedFalseFalsePassed
ignoredTrueTruePassed
trigger opensTrueTruePassed
trigger closesFalseFalsePassed
inside descendantTrueTruePassed
parameterized descendant depthTrueTruePassed
repeat inside descendantTrueTruePassed

SHA-256 / 7a045de732a8956f9c0381ac510e7759e33401a66e89faa24402a37a06cf6dbd

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

Case digest / 2e484b5c8bba7e83e70da177b82eb685045a1e12eae85071b36cec9e8a6eff3f