Tracked internally as FP-10 (https://alphawavesystems.atlassian.net/browse/FP-10).
Problem
_tap/_doubleTap/_longPress in probe_agent/lib/src/executor.dart (~lines 311-402) resolve a selector to an Element via ProbeFinder (which already filters to mounted + current-route + non-zero-size elements — the separate off-screen/scroll-into-view gap is tracked as PT-03 in IMPROVEMENT_TASKS.md) and then interact with it one of two ways:
- If
element.widget is Semantics, _tryDirectTap walks down to a GestureDetector/InkResponse descendant and calls widget.onTap!() directly — no hit-testing at all.
- Otherwise (or if
_tryDirectTap finds nothing), a synthetic pointer gesture fires at the element's localToGlobal center via _createGesture.
Neither path checks whether that screen coordinate is actually the topmost hit. So tap can succeed against a button that's mounted, on the current route, and non-zero-size, but visually covered by something a real user's finger would hit first — a modal barrier, a blocking/loading overlay, a snackbar, a bottom sheet, another Stack sibling drawn on top. The test reports success even though a human tester could not have produced that outcome.
Correction to an earlier framing
An earlier proposal was to reorder tap dispatch (hit-tested gesture first, direct-invoke as fallback). That's wrong — see PT-04/PT-05 in IMPROVEMENT_TASKS.md (confirmed: a real hit-tested tap on a Semantics-wrapped widget didn't reliably focus text fields or invoke onTap on buttons/drawer-triggers; _tryDirectTap exists specifically to fix that) and PT-22 (closed — a crash that looked like evidence against the direct-invoke path turned out to be an unrelated, rare Flutter framework race, irrelevant in release builds). No evidence in this repo's own history supports reordering; doing so would reintroduce PT-04/PT-05.
Correct scope
Add an occlusion guard that runs before either tap path fires, independent of invocation order: hit-test the target's screen-space center (e.g. via the current binding's hitTestInView) and confirm the resolved element/RenderObject is actually part of the hit-test result at that point. If occluded, tap should fail loudly (e.g. "target is covered by <widget>, not tappable") instead of silently proceeding via _tryDirectTap's bypass.
Suggested implementation
- New helper, e.g.
_isOccluded(Element element, Offset point), in executor.dart or finder.dart (alongside _isVisible).
- Called from
_tap, _doubleTap, _longPress before either invocation path.
- Open question: hard-fail vs. warn-and-proceed — given PT-02's theme (executor silently no-ops on malformed input) and this project's general direction toward loud failures, hard-fail seems consistent, but needs a maintainer call.
- Test plan: Dart widget test in
probe_agent/test/ — a button under a Stack covered by an opaque sibling / ModalBarrier, asserting tap fails rather than invoking the covered button's onTap.
Files likely touched
probe_agent/lib/src/executor.dart (_tap, _doubleTap, _longPress, new occlusion helper)
probe_agent/lib/src/finder.dart (if the occlusion check lives here instead, alongside _isVisible)
probe_agent/test/ (new coverage)
IMPROVEMENT_TASKS.md (candidate PT-28, following existing convention)
Related but distinct
PT-03 (scroll-to-element / off-screen targeting) covers the target not being in the viewport at all. This issue covers the target being in the viewport but covered by something else at that exact point. Same underlying principle (tap should only succeed where a real user's tap would), different code paths, likely different PRs.
Tracked internally as FP-10 (https://alphawavesystems.atlassian.net/browse/FP-10).
Problem
_tap/_doubleTap/_longPressinprobe_agent/lib/src/executor.dart(~lines 311-402) resolve a selector to anElementviaProbeFinder(which already filters to mounted + current-route + non-zero-size elements — the separate off-screen/scroll-into-view gap is tracked as PT-03 inIMPROVEMENT_TASKS.md) and then interact with it one of two ways:element.widget is Semantics,_tryDirectTapwalks down to aGestureDetector/InkResponsedescendant and callswidget.onTap!()directly — no hit-testing at all._tryDirectTapfinds nothing), a synthetic pointer gesture fires at the element'slocalToGlobalcenter via_createGesture.Neither path checks whether that screen coordinate is actually the topmost hit. So
tapcan succeed against a button that's mounted, on the current route, and non-zero-size, but visually covered by something a real user's finger would hit first — a modal barrier, a blocking/loading overlay, a snackbar, a bottom sheet, anotherStacksibling drawn on top. The test reports success even though a human tester could not have produced that outcome.Correction to an earlier framing
An earlier proposal was to reorder tap dispatch (hit-tested gesture first, direct-invoke as fallback). That's wrong — see PT-04/PT-05 in
IMPROVEMENT_TASKS.md(confirmed: a real hit-tested tap on a Semantics-wrapped widget didn't reliably focus text fields or invokeonTapon buttons/drawer-triggers;_tryDirectTapexists specifically to fix that) and PT-22 (closed — a crash that looked like evidence against the direct-invoke path turned out to be an unrelated, rare Flutter framework race, irrelevant in release builds). No evidence in this repo's own history supports reordering; doing so would reintroduce PT-04/PT-05.Correct scope
Add an occlusion guard that runs before either tap path fires, independent of invocation order: hit-test the target's screen-space center (e.g. via the current binding's
hitTestInView) and confirm the resolved element/RenderObject is actually part of the hit-test result at that point. If occluded,tapshould fail loudly (e.g."target is covered by <widget>, not tappable") instead of silently proceeding via_tryDirectTap's bypass.Suggested implementation
_isOccluded(Element element, Offset point), inexecutor.dartorfinder.dart(alongside_isVisible)._tap,_doubleTap,_longPressbefore either invocation path.probe_agent/test/— a button under aStackcovered by an opaque sibling /ModalBarrier, asserting tap fails rather than invoking the covered button'sonTap.Files likely touched
probe_agent/lib/src/executor.dart(_tap,_doubleTap,_longPress, new occlusion helper)probe_agent/lib/src/finder.dart(if the occlusion check lives here instead, alongside_isVisible)probe_agent/test/(new coverage)IMPROVEMENT_TASKS.md(candidate PT-28, following existing convention)Related but distinct
PT-03 (scroll-to-element / off-screen targeting) covers the target not being in the viewport at all. This issue covers the target being in the viewport but covered by something else at that exact point. Same underlying principle (tap should only succeed where a real user's tap would), different code paths, likely different PRs.