fix(ios): keep the bridge while a surface host only lingers

Merged
TH
9h ago
+288-3

Summary

On the iOS simulator, the snapshot route sent every capture to XCTest while any registered system-surface host process was running for the device. Password AutoFill at sign-in starts SafariViewService and leaves it running for the rest of the session. So after logging in to Bluesky, every capture took the runner: 3–8 s per capture on its feed, against ~200 ms on the host AX bridge.

When a host process is running, the route now asks the live runner for every registered host's XCUIApplication.state. That's the same predicate the runner uses to serve a surface in place (ADR 0004, #2438).

  • All hosts off screen: the capture stays on the bridge.
  • A foreground host, an unreadable state, or no ready runner: the capture keeps the runner route.

The read is fenced with expectedRunnerSessionId and bounded to 1 s, so it never starts or replaces a runner. Codex design review required checking all hosts and the no-start fence. 5 files, including the ADR 0004 amendment.

Validation

  • pnpm check:affected --run --head 4d5422f27e: passed.
  • The new route tests fail on origin/main source; the probe tests cover the all-hosts rule, no-start, malformed state, failure and cancellation.
  • Live, iOS 27.0 simulator:
    • Bluesky after login, SafariViewService lingering (same pid throughout): bridge at 132–192 ms with two ~6 ms appState reads; base used XCTest (system-surface-host-lingering).
    • Agent Device Tester Apple Pay sheet (PassbookUIService): sheet presented → runner served the Wallet sheet with disclosure; dismissed → bridge, 135–189 ms, app shows dismissed; same pid presenting again → runner.

🤖 Generated with Claude Code