Repository navigation
Description
What happens today
wait <target> <timeoutMs> charges its whole budget to whatever the polls cost, including work that is ours rather than the app's: the XCTest runner's first command on a freshly launched app, runner start or restart, and AX-bridge target discovery. The deadline also cancels the poll in flight, so a first poll that costs more than the budget ends with zero completed observations.
Four iOS Smoke runs since 2026-09-05 21:00 show it on the first wait text "Agent Device Tester" after a cold open --relaunch (wait_capture_stalled, readableCaptures: 0):
- main
80997b6b: https://github.com/callstack/agent-device/actions/runs/33992502696 - main
f30328d0: https://github.com/callstack/agent-device/actions/runs/34014253127 - PR fix(ios): confirm alerts without repeating activation #2326: https://github.com/callstack/agent-device/actions/runs/34015120372
- branch
fix/2314-memoize-platform-runtime-la: https://github.com/callstack/agent-device/actions/runs/34016148244
Request log of the third run (sessions/ios-e2e-smoke-14ax/requests/45ad58af8fd6970d.ndjson in ios-artifacts):
| phase | cost |
|---|---|
runner findText on the just-launched app |
5.8s |
AX-bridge target discovery, xcrun timed out after 3000ms |
3.4s |
| fallback XCTest snapshot | cancelled at the 10s deadline |
An earlier instance of the same accounting, before #2279, is https://github.com/callstack/agent-device/actions/runs/33984867542 (sessions/ios-e2e-smoke-ohv/requests/6af0b46614602883.ndjson): the runner stopped accepting connections mid-wait, the daemon invalidated and relaunched it, and the wait was cancelled 0.7s after xcodebuild started, reported as wait_capture_stalled rather than a runner restart because the cancelled capture carried no restart evidence.
#2331 takes the discovery probe off the per-capture critical path. #2325 gives open its own startup budget. Neither changes what a wait budget pays for.
What to change
- Account startup and readiness time (runner start, runner restart, target discovery, first-command warm-up) separately from observation time in the wait loop, and surface both in the failure details.
- Keep one overall deadline and cancellation. A hung capture must still end at the deadline, so this is not a guarantee of "at least one completed observation".
- When infrastructure prevented any observation before the deadline, say so explicitly with its own reason or typed detail, distinct from "the device gave no readable picture", and keep
wait_capture_stalledfor the latter. - Decide whether readiness time should extend the observation window (bounded, once) or only be reported. The report is the prerequisite either way.
Related
- fix(ios): stop charging every capture for a slow Simulator app discovery #2331 discovery probe off the critical path
- fix(ios): honor the startup budget through a cold Simulator boot #2325
openstartup deadlines - perf(ios): derive runner demand for Simulator opens and stop observation from awaiting the runner #2329 observation runner-free, launch grace
- fix(wait): surface runner restart timeout evidence #2113 runner restart evidence on wait timeouts
Activity
feat(wait): carry a per-poll timeline in timeout failures (#2344)
fix(wait): report readiness work that consumed the wait budget
fix(wait): report readiness work that consumed the wait budget
fix(wait): report readiness work that consumed the wait budget (#2893)
thymikee commentedon Oct 2, 2026
Close the remaining scope as not planned, rather than mark the original accounting request implemented.
#2893 shipped the useful failure distinction: cancellation during runner start or Simulator target discovery carries
wait_readiness_exhaustedandreadinessPhase; runner restart remains separately classified. #2982 makes a deadline-bounded wait return on time without repeatedly killing and rebuilding a starting runner. #3098 documents those outcomes. Completed-poll readiness-versus-observation accounting and first-command warm-up attribution remain unimplemented.The decision is to retain one overall timeout including readiness, with no observation-window extension. The existing per-poll outcome/duration timeline and owning request diagnostic timers provide the investigation path. A public breakdown would need a precise attribution contract for overlapping detached starts, bridge discovery, retries and first-command work; a first slow command alone does not establish which part was infrastructure. That extra machinery is not justified as open-ended residual work now that the actionable cancellation distinction and startup reuse have shipped.
Reopen a bounded report-only request if a captured failure cannot be diagnosed from those existing fields and request phases. The reopening case should name the missing measurement and its producer, preserve the single deadline/cancellation, and define a non-overlapping accounting rule. This closure makes no claim that the original completed-poll accounting or warm-up work landed.