fix(ios): keep the bridge while a surface host only lingers
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/mainsource; 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
appStatereads; 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.
- Bluesky after login, SafariViewService lingering (same pid throughout): bridge at 132–192 ms with two ~6 ms
🤖 Generated with Claude Code