fix(ios): size the sole display from the system app, not the runner's UIScreen
Summary
Fixes #4091. Synthesized landscape presses landed a fixed offset away from the requested point and still reported ok. soleDisplayFrame read UIScreen.main in the runner process. The generated AgentDeviceRunnerUITests-Runner.app has no launch screen, so that read can come back in compatibility mode (768x1024 on an iPad).
- Display source: the AXRuntime system application frame, normalized to portrait. It is read once per command (~0.6ā0.9 ms, measured), and only for turns that use the box (landscape and upside-down).
- Unreadable display: portrait needs no box, so a tap is sent as given, even with no window. In landscape, an app the sole-screen rule (
RunnerResolveSoleScreen) admits uses its window. With no window, or a placed iPad window (#3899), the gesture returnsUNSUPPORTED_OPERATIONbefore dispatch. Each case logsAGENT_DEVICE_RUNNER_APP_SCREEN_UNRESOLVED stage=rotation-basis. - Unchanged: multi-screen hosts keep the window route. No new private API is added; iPhone synthesis now also uses AXRuntime.
Rejected: a launch-screen key for the runner, which would mean patching Xcode's runner template and could not be proven here.
Validation
Base a42bc35282, head 00d36437ba.
- The compatibility-mode test fails on base and passes on head.
- AX-failure tests: portrait taps are dispatched, with or without a window. In landscape, iPhone dispatches about the window, a placed iPad window refuses, and a missing window refuses. Each test fails when its branch is removed.
- The per-command cache test passes.
- Live run with the branch runner on iOS 27.0: iPad 3/3 per orientation, iPhone 17 3/3 per landscape side.
- Last local receipt:
check:affected --run --head bf0b1ca48dpassed all runnable checks,exactCommit=true(run-jWH5SD). The head00d36437bachanges only two Swift test comments, which the gate does not compile. Gate reruns on that head failed only in unrelated TS unit tests, hit by strayGET /traffic on the host. CI is green on00d36437ba. Swift runner gates run in CI.
Unverified: the live compatibility-mode offset did not reproduce on these runtimes. Possible follow-up: a unit test that fakes a two-screen XCUIScreen.screens; the single-screen guard is enforced in RunnerResolveSoleScreen and synthesizedRotationFrame.
š¤ Generated with Claude Code