You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up from #1820 (#1781 A1), asked for in its review.
Today
buildScrollGesturePlan (packages/contracts/src/scroll-gesture.ts) keeps scroll gestures out of the outer DEFAULT_EDGE_PADDING_FRACTION = 10 % of each axis. That fraction is a proxy for the real invariant — a scroll must never touch down inside a system bar — tuned to exceed the tallest known bar (Pixel 7 cutout status bar 136/2400 = 5.7 %, iPhone Dynamic Island 59/852 = 6.9 %). It lives in a device-agnostic pure planner because the viewport it receives is the raw application window, which on edge-to-edge apps (API 35+, every system app, iOS) starts at y=0 and includes the bars. #1820 raised it from 5 % after scroll up 3 on a Pixel 7-geometry emulator touched down at y=120, inside the bar, and pulled the notification shade instead of scrolling.
Costs of the proxy: every user pays 10 points of max single-gesture travel (90 % → 80 % for amount ≥ 0.8) whether or not their device has a cutout, and a device with a taller bar than the tuned constant reintroduces the shade pull silently.
Proposed
The layer that knows where the bars are should say so:
Android — GestureViewportReader.read() (android/snapshot-helper/.../GestureViewportReader.java) already iterates UiAutomation.getWindows() and keeps only TYPE_APPLICATION; the StatusBar / NavigationBar system windows are right there to subtract (or read the status/navigation-bar insets). Expose content-safe bounds alongside the raw window bounds in the viewport action (helper protocol/API version bump).
Apple — the runner has safe-area insets; report the same content-safe rect in the resolved scroll frame.
Planner — clamp scroll travel to the content-safe rect and return DEFAULT_EDGE_PADDING_FRACTION to a small generic margin (the original 5 % or less). Keep the raw window rect for absolute swipe/gesture validation so an agent can still deliberately swipe down from the status bar.
The masking in #1781 A1 came from discoverReplaySourcePaths (src/daemon/replay-source-discovery.ts) enumerating directory inputs in native readdir order (03,05,06,…,04 on the ubuntu runner vs 01…06 locally). The header documents this as deliberate Maestro parity, and it is accurate — Maestro's WorkspaceExecutionPlanner uses Files.walk and names the result unsortedFlowFiles, no sort. Sorting directory inputs would make results reproducible across machines but is a deliberate departure from that parity; decide it here rather than silently. Order-sensitive tests currently pass explicit positionals (see session-test-suite.test.ts).
Follow-up from #1820 (#1781 A1), asked for in its review.
Today
buildScrollGesturePlan(packages/contracts/src/scroll-gesture.ts) keeps scroll gestures out of the outerDEFAULT_EDGE_PADDING_FRACTION= 10 % of each axis. That fraction is a proxy for the real invariant — a scroll must never touch down inside a system bar — tuned to exceed the tallest known bar (Pixel 7 cutout status bar 136/2400 = 5.7 %, iPhone Dynamic Island 59/852 = 6.9 %). It lives in a device-agnostic pure planner because the viewport it receives is the raw application window, which on edge-to-edge apps (API 35+, every system app, iOS) starts at y=0 and includes the bars. #1820 raised it from 5 % afterscroll up 3on a Pixel 7-geometry emulator touched down at y=120, inside the bar, and pulled the notification shade instead of scrolling.Costs of the proxy: every user pays 10 points of max single-gesture travel (90 % → 80 % for
amount ≥ 0.8) whether or not their device has a cutout, and a device with a taller bar than the tuned constant reintroduces the shade pull silently.Proposed
The layer that knows where the bars are should say so:
GestureViewportReader.read()(android/snapshot-helper/.../GestureViewportReader.java) already iteratesUiAutomation.getWindows()and keeps onlyTYPE_APPLICATION; theStatusBar/NavigationBarsystem windows are right there to subtract (or read the status/navigation-bar insets). Expose content-safe bounds alongside the raw window bounds in theviewportaction (helper protocol/API version bump).DEFAULT_EDGE_PADDING_FRACTIONto a small generic margin (the original 5 % or less). Keep the raw window rect for absoluteswipe/gesturevalidation so an agent can still deliberately swipe down from the status bar.contracts/fixtures/scroll-gesture.json(added in fix(scroll): keep saturated scroll gestures out of the status bar; gate Android replays from android/emulator (#1781 A1) #1820) is where the new vectors go — both the TS and Swift suites iterate it.Also on this issue: suite discovery order
The masking in #1781 A1 came from
discoverReplaySourcePaths(src/daemon/replay-source-discovery.ts) enumerating directory inputs in native readdir order (03,05,06,…,04on the ubuntu runner vs01…06locally). The header documents this as deliberate Maestro parity, and it is accurate — Maestro'sWorkspaceExecutionPlannerusesFiles.walkand names the resultunsortedFlowFiles, no sort. Sorting directory inputs would make results reproducible across machines but is a deliberate departure from that parity; decide it here rather than silently. Order-sensitive tests currently pass explicit positionals (seesession-test-suite.test.ts).