FA-8171 / Disclosure widgets / Open access
Tooltip trigger and dismissal: Blur hides a tooltip that remains pointer-hovered · case 01
Blur hides a tooltip that remains pointer-hovered.
ROOT CAUSE
The blur operation uses `if event == 'blur': return False` where the contract requires `if event == 'blur': return bool(hovered)`.
VERIFIED REPAIR
Implement the blur operation as `if event == 'blur': return bool(hovered)`.
Unsuccessful approach: Always keeping it open leaves abandoned tooltips visible.
Case contract
Disabled triggers hide content; Escape dismisses regardless of trigger state; focus/enter open; blur and leave preserve the other active trigger.
Why this case matters
A deterministic model of tooltip trigger and dismissal; 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(state, event, focused, hovered, disabled):
if disabled: return False
if event == 'escape': return False
if event == 'focus': return True
if event == 'enter': return True
if event == 'blur': return False
if event == 'leave': return bool(focused)
return state
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('disable', solve(True, 'none', False, False, True), False)
check('escape', solve(True, 'escape', True, True, False), False)
check('focus', solve(False, 'focus', True, False, False), True)
check('blur hovered', solve(True, 'blur', False, True, False), True)
check('blur abandoned', solve(True, 'blur', False, False, False), False)
check('leave focused', solve(True, 'leave', True, False, False), True)
check('leave abandoned', solve(True, 'leave', False, False, False), False)
check('parameterized remaining trigger', solve(True, 'blur', False, bool(N%2), False), bool(N%2))
for repetition in range(N):
check('repeat leave abandoned', solve(True, 'leave', False, False, False), False)
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 |
|---|---|---|---|
| disable | False | False | Passed |
| escape | False | False | Passed |
| focus | True | True | Passed |
| blur hovered | False | True | Failed |
| blur abandoned | False | False | Passed |
| leave focused | True | True | Passed |
| leave abandoned | False | False | Passed |
| parameterized remaining trigger | False | True | Failed |
| repeat leave abandoned | False | False | Passed |
SHA-256 / b260936489aea2c6f7f09071a4f1e0add65e1d681ebaf07df1d71f95aac88f71
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(state, event, focused, hovered, disabled):
if disabled: return False
if event == 'escape': return False
if event == 'focus': return True
if event == 'enter': return True
if event == 'blur': return True
if event == 'leave': return bool(focused)
return state
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('disable', solve(True, 'none', False, False, True), False)
check('escape', solve(True, 'escape', True, True, False), False)
check('focus', solve(False, 'focus', True, False, False), True)
check('blur hovered', solve(True, 'blur', False, True, False), True)
check('blur abandoned', solve(True, 'blur', False, False, False), False)
check('leave focused', solve(True, 'leave', True, False, False), True)
check('leave abandoned', solve(True, 'leave', False, False, False), False)
check('parameterized remaining trigger', solve(True, 'blur', False, bool(N%2), False), bool(N%2))
for repetition in range(N):
check('repeat leave abandoned', solve(True, 'leave', False, False, False), False)
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 |
|---|---|---|---|
| disable | False | False | Passed |
| escape | False | False | Passed |
| focus | True | True | Passed |
| blur hovered | True | True | Passed |
| blur abandoned | True | False | Failed |
| leave focused | True | True | Passed |
| leave abandoned | False | False | Passed |
| parameterized remaining trigger | True | True | Passed |
| repeat leave abandoned | False | False | Passed |
SHA-256 / 7270c671b17ac8f21bdebb3b62b8542e1b94fc033c65b9a4beaa4e7f91146a98
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(state, event, focused, hovered, disabled):
if disabled: return False
if event == 'escape': return False
if event == 'focus': return True
if event == 'enter': return True
if event == 'blur': return bool(hovered)
if event == 'leave': return bool(focused)
return state
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('disable', solve(True, 'none', False, False, True), False)
check('escape', solve(True, 'escape', True, True, False), False)
check('focus', solve(False, 'focus', True, False, False), True)
check('blur hovered', solve(True, 'blur', False, True, False), True)
check('blur abandoned', solve(True, 'blur', False, False, False), False)
check('leave focused', solve(True, 'leave', True, False, False), True)
check('leave abandoned', solve(True, 'leave', False, False, False), False)
check('parameterized remaining trigger', solve(True, 'blur', False, bool(N%2), False), bool(N%2))
for repetition in range(N):
check('repeat leave abandoned', solve(True, 'leave', False, False, False), False)
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 |
|---|---|---|---|
| disable | False | False | Passed |
| escape | False | False | Passed |
| focus | True | True | Passed |
| blur hovered | True | True | Passed |
| blur abandoned | False | False | Passed |
| leave focused | True | True | Passed |
| leave abandoned | False | False | Passed |
| parameterized remaining trigger | True | True | Passed |
| repeat leave abandoned | False | False | Passed |
SHA-256 / c75368672f7ce71ffb7a703eb2ba69ccbf407ff753a76cc5311461e6cb8448b7
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:18.983964+00:00.
Case digest / 20154f13925fd2fe3ecf251b3ddce9628313fee4e33f540d3679d69f214d37c0