fix(ios): let a Simulator open wait out a slow app discovery before observing the launch - #2838
Conversation
…bserving the launch The launch-observation probe read a target discovery that outlasted one 1.5 s wait slice as an unobservable app. On a loaded host simctl launchctl list takes longer than that, so open --relaunch returned before the discovery, the AX-bridge preparation or the first bridge connection, and the first wait after it paid all three in one poll behind a runner findText. On iOS smoke CI that poll consumed the whole 10 s budget (wait_capture_stalled, captures: 1) at step 7 of smoke:automation-input. The probe now keeps joining the running discovery, bounded by the discovery's own deadline; any other resolution failure still ends it at once. The discovery-pending reason and its predicate move to the resolver that owns them, shared by the capture route.
Size Report
Startup median (7 runs, lower is better):
|
|
Reviewed at 84c47cc. The open now waits out a slow app discovery within its deadline, and a short Not blocking: iOS Smoke was still running at review time. It exercises this route ( |
|
|
Follow-up on the non-blocking note about |
…readable am start -W returns when the activity draws its first frame, which can be a splash or an empty root while a React Native app still mounts. The first capture after a cold open --relaunch then saw a content-poor tree and spent its own budget on re-captures. The open now captures the launched app through the interactor snapshot path, whose content verdict and bounded re-capture decide readiness, and reports postOpenObservation. An app that stays unreadable, or a failed capture, still opens as unobservable. Refs #1571 (iOS half: #2838).
…readable am start -W returns when the activity draws its first frame, which can be a splash or an empty root while a React Native app still mounts. The first capture after a cold open --relaunch then saw a content-poor tree and spent its own budget on re-captures. The open now captures the launched app through the interactor snapshot path, whose content verdict and bounded re-capture decide readiness, and reports postOpenObservation. An app that stays unreadable, or a failed capture, still opens as unobservable. Refs #1571 (iOS half: #2838).
…readable am start -W returns when the activity draws its first frame, which can be a splash or an empty root while a React Native app still mounts. The first capture after a cold open --relaunch then saw a content-poor tree and spent its own budget on re-captures. The open now captures the launched app through the interactor snapshot path, whose content verdict and bounded re-capture decide readiness, and reports postOpenObservation. An app that stays unreadable, or a failed capture, still opens as unobservable. Refs #1571 (iOS half: #2838).
…readable am start -W returns when the activity draws its first frame, which can be a splash or an empty root while a React Native app still mounts. The first capture after a cold open --relaunch then saw a content-poor tree and spent its own budget on re-captures. The open now captures the launched app through the interactor snapshot path, whose content verdict and bounded re-capture decide readiness, and reports postOpenObservation. An app that stays unreadable, or a failed capture, still opens as unobservable. Refs #1571 (iOS half: #2838).
…readable (#2895) * fix(android): return from an app open only after the launched app is readable am start -W returns when the activity draws its first frame, which can be a splash or an empty root while a React Native app still mounts. The first capture after a cold open --relaunch then saw a content-poor tree and spent its own budget on re-captures. The open now captures the launched app through the interactor snapshot path, whose content verdict and bounded re-capture decide readiness, and reports postOpenObservation. An app that stays unreadable, or a failed capture, still opens as unobservable. Refs #1571 (iOS half: #2838). * fix(android): bound and type the launch observation of an app open The open's launch capture now runs behind an injected launch observation port with a fixed 6 s window of its own, separate from the caller's cancellation. Only a content verdict, a system surface over the app, or the window running out reads as unobservable. Any other capture failure is a typed probe-failed result that the open reports and survives. The capture is transient: it borrows a running helper session and stops only a session it started, installs no helper, and does not retire the helper after a content verdict. A URL open reports no observation, and an app open whose package cannot be read reports app-unidentified. PostOpenObservation is one documented union in the lifecycle contract, shared with the Apple owner. Refs #1571 * fix(android): let the launch settle window end re-captures, never helper work The open's 6 s window reached the snapshot helper as an abort, so a window that closed during a cold helper start or a borrowed capture tore the helper down and left the next read to recover it. The window is now a settle deadline on the transient capture: the content re-capture loop starts no attempt after it, while helper start, capture and teardown keep their own budgets and only the caller's signal cancels them. The transient read replaces the injected port, the borrow session scope and the interactor-side install mapping: the capture itself keeps a session it found, releases one it started, installs no helper and does not retire it after a content verdict. Refs #1571 * fix(android): keep a missing-helper refusal out of capture failure recovery A transient capture on a device without the current helper refused at the install check, and that refusal went through the capture failure handler, which logged an error and force-stopped the helper runtime on every new device's first open. The refusal now reaches the caller directly. The transient-capture tests move to their own file, so snapshot.test.ts stays under the test-file size ratchet. Refs #1571
Summary
iOS Smoke
smoke:automation-inputstep 7 (wait text "Agent Device Tester" 10000right afteropen --relaunch) failed 15 times 09-14..23, 4 on main, alwayswait_capture_stalledwithcaptures: 1. The CI request logs (runs 35314997169, 35462595081, 35589604091, 35643318229, 35869384788) show one poll: runnerfindText(2.6–3.9 s), then target discovery, then the first AX-bridge preparation (ios.snapshot-source.prepare1.7 s, orbridge-preparation-pending), then the first bridge connection until the deadline cancelled it. The runner was already live; no restart.Cause: the open's launch-observation probe treated
simulator-target-discovery-pending(one 1.5 s slice) as an unobservable app. So the open returned before the discovery, bridge preparation and bridge connection, and the wait paid for all three.Fix: the probe keeps joining the running discovery, bounded by the discovery's own 15 s deadline. Any other resolution failure still ends it at once. The pending predicate moves to
snapshot-target.ts, which owns the reason. 7 files.Validation
Tested at
84c47cca78:pnpm check:affected --runpassed.an open waits out a slow app discovery…) and the observability test fail when the pending check is reverted toreturn undefined.postOpenObservation: observableand 8/8 waits passed (63–475 ms). A never-matching wait's first poll took 0.46–0.52 s. Before the fix, 5/9 opens wereunobservable(settle ≈ 1.8 s = the slice), and the first poll carried the bridge preparation and spawn (3.6 s). The full 10 s stall did not reproduce locally.