22
33## Status
44
5- Accepted (2026-10-03). Opt-in; XCTest stays the default app-session backend.
5+ Accepted (2026-10-03). Opt-in; XCTest stays the default app-session backend. Evidence gap: pointer
6+ event delivery was re-measured on 2026-10-05 (#3213 ); Electron, Catalyst, and wheel delivery with
7+ window fields are unmeasured.
68
79## Rules at a glance
810
@@ -47,10 +49,17 @@ windows.
4749
4850** Pointer delivery.** Events posted to a process with ` CGEventPostToPid ` were measured against
4951Calculator (SwiftUI): mouse events were dropped whether the app was frontmost or in the background,
50- and wheel events were dropped in the background, while every post reported success. Keyboard events
51- were accepted by Calculator and by Electron apps in the background. A fallback that cannot be
52- observed to work would turn "nothing happened" into success, so pointer actions have no event
53- fallback.
52+ and wheel events were dropped in the background, while every post reported success. Those events
53+ named no window. A re-measurement against a background AppKit, SwiftUI, and WKWebView fixture
54+ (#3213 ) found the window fields decide delivery, not the API: with the target window number in
55+ event field 51 and either field 58 or a window-local location (` CGEventSetWindowLocation ` ), the
56+ public ` CGEventPostToPid ` delivered click, double-click (click state in field 1), and secondary
57+ click to all three, 54 of 54, without activating the app or changing the frontmost app. Fields 51
58+ and 58 are undocumented. Drags and WKWebView button clicks were delivered only after a private
59+ focus-without-raise activation. Keyboard events were accepted by Calculator and by Electron apps in
60+ the background. A dropped post still reports success, and a fallback that cannot be observed to
61+ work would turn "nothing happened" into success, so pointer actions have no event fallback until
62+ an event path verifies its outcome.
5463
5564** Target resolution.** The helper hit-tests inside the session app (other apps' windows above it do
5665not answer) and walks at most four ancestors for a text input or a pressable control role.
@@ -76,9 +85,11 @@ pointer would need a long-lived helper. `AGENT_DEVICE_MACOS_GHOST_CURSOR=0` disa
7685
7786- ** Suppressing Automation Mode.** ` automationmodetool ` removes the authentication prompt, not the
7887 overlay, and the runner still owns the pointer.
79- - ** Private SkyLight event delivery** (` SLEventPostToPid ` , focus-without-raise). It would cover
80- pointer-only controls, but it is private API that can break with any macOS release. Revisit only
81- with evidence of apps the accessibility path cannot drive.
88+ - ** Private SkyLight event delivery** (` SLEventPostToPid ` , focus-without-raise). Posting through
89+ ` SLEventPostToPid ` delivered nothing the same fields did not deliver through the public
90+ ` CGEventPostToPid ` (#3213 ). Focus-without-raise (` SLPSPostEventRecordTo ` ) made the target
91+ AppKit-active while the frontmost app kept the user's keystrokes, and it was the only way drags
92+ reached the fixture, but it is private API that can break with any macOS release.
8293- ** Native by default.** The accessibility path cannot express drags, holds, or double-clicks, and
8394 apps with sparse accessibility trees still need the runner. Defaults change only with coverage
8495 evidence across app frameworks.
0 commit comments