fix(selectors): refuse a zero-area press target instead of tapping its origin
AI
7h ago+78+78+01234567890123456789-1-1-0123456789
Summary
A selector that matched a 0×0 node (SpringBoard keeps page-1 icons that way on page 2) resolved to the rect's origin, so press tapped (0, 0) and reported success. resolveInteractionTouchPoint now returns invalid for a target with zero width or zero height. That surfaces as the existing target_bounds_invalid refusal with dispatched: 'no'. normalizeRect is unchanged, because other readers use zero-size rects.
This applies to every command that aims through resolveNodeTouchPoint: press/click, and fill or longpress on a selector or ref.
3 files: the resolver, its unit test (zero width, zero height, both, plus a 1×1 target that still resolves), and a command-level press case that refuses and records no tap.
Closes #3841
Validation
- Tested SHA
5bbc20cd3(basec48d27a81). pnpm check:affected --run --head 5bbc20cd3: passed,exactCommit=true.- Both new tests fail on base (
resolvedinstead ofinvalid; "Missing expected rejection") and pass at head. - Live, at
5bbc20cd3: iPhone 17 Pro Max simulator, iOS 26.5,open com.apple.springboard, home screen on "Page 2 of 2",snapshotQuality.backend: tree.snapshotshows the page-1 icons (Calendar, Photos, Settings, …) at0,0 0x0,hittable: false. This layout has no Clock, soSettingsstands in.press 'label="Settings"'→ exit 1,COMMAND_FAILED,reason: target_bounds_invalid,dispatched: no, "Selector label="Settings" resolved to invalid bounds". The request log shows onlyappState,appState,snapshotsent to the runner, and no tap.press 'label="Watch"'(page 2,72x94) → exit 0, "Tapped label="Watch" (170, 139)" (its center) and opened Watch.
🤖 Generated with Claude Code