FAILURE MAP
← Case archive

FA-8196 / Overlay interactions / Open access

Popover outside-interaction handling: Trigger interaction can close but cannot open · case 01

Trigger interaction can close but cannot open.

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

ROOT CAUSE

The trigger operation uses `if trigger in target_path: return False` where the contract requires `if trigger in target_path: return not opened`.

VERIFIED REPAIR

Implement the trigger operation as `if trigger in target_path: return not opened`.

Unsuccessful approach: Always opening removes toggle-to-close behavior.

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 False
    if event != 'pointerdown': return opened
    if trigger in target_path: return False
    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 opensFalseTrueFailed
trigger closesFalseFalsePassed
inside descendantTrueTruePassed
parameterized descendant depthTrueTruePassed
repeat inside descendantTrueTruePassed

SHA-256 / d87ed938dc341605ef41ae6704cc961f1c0397d725e0ef5186d77bcaad37f42c

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 False
    if event != 'pointerdown': return opened
    if trigger in target_path: return True
    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 closesTrueFalseFailed
inside descendantTrueTruePassed
parameterized descendant depthTrueTruePassed
repeat inside descendantTrueTruePassed

SHA-256 / 0ac96b3ce24a313b03c4d7fb5c5621c4bd85c65bcc4168b0a63a1958a989178e

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

Case digest / 2e4f317e73758d7ccc2980b33d356d1dac5f9ff8bc4baaef291fc613fb7a07e4