Blolabel
← Notes
analysis 6 min read

WebDriverAgent can hand a second app crash detection now; phoneshell's open_app never asks for it

WDA 16.15.0 added a flag so a later launch can claim the session's "application under test" status, with its quiescence inheritance, alert monitoring and crash detection. phoneshell already duplicates the first two itself. It has nothing for the third, and never sets the flag.

WebDriverAgent 16.15.0, released 12 October 2026, added an opt-in flag, asAppUnderTest, so a later /wda/apps/launch call can make its app the one WDA tracks as the application under test. phoneshell's two session-creation call sites never pass a bundle id on purpose, and its three-attempt open_app never passes the new flag either. The app WDA considers "under test" during a phoneshell run is not the app the agent is looking at. It is whatever was in front the last time a stale session got rebuilt.

Abstract technical illustration of a tracking beam that locks onto one object in a cluster while two other identical objects drift past unlit

What WDA 16.15.0 changed, and on which date

PR #1289, merged 12 October 2026 as commit f5c0688, adds asAppUnderTest to the /wda/apps/launch route. Before this release, a session's application under test could only be set once, at session creation, by passing a bundleId capability. Every later /wda/apps/launch call started a new app without ever replacing that designation. The PR description states the three things being application-under-test buys an app: later launches inherit its quiescence setting, system alerts over it are respected, and its unexpected termination is reported as a crash (an FBApplicationCrashedException) rather than read as an ordinary state change. The flag defaults to false, so WDA's own authors describe existing behaviour as unchanged unless a caller opts in.

Where asAppUnderTest could plug in, and where phoneshell never reaches for it POST /session bundleId: none wda/client.py:248 → testedApplication = nil no bundleId was given FBSession.m launch path open_app(target), actions.py:845 1 launch_unattached session-free, cannot touch it 2 activate_app no asAppUnderTest param at all 3 launch_app(bundle) asAppUnderTest never sent, defaults false Exception: a stale-session recovery (wda/client.py:190-197, "happens routinely") reads the real foreground app and sets default_bundle_id to it, so the NEXT create_session() carries that bundle id in. Whichever app that happened to be is "the" application under test, not necessarily the one the agent is using now.

phoneshell's create_session (wda/client.py:248) resolves its bundle id as bundle_id or self.default_bundle_id, and both call sites (lines 208 and 234) pass neither. default_bundle_id starts at None. It only ever gets set inside the stale-session recovery branch (lines 190-197), whose own comment explains why it does not simply press home and re-attach: "a session created with no bundleId makes WDA switch the app under test, which sends the phone to the home screen and closes whatever was just opened." So the mechanism that occasionally gives phoneshell a tracked application under test is a side-effect workaround for a different WDA behaviour, not a deliberate choice, and it carries over whatever was in front at the moment of recovery, which can be stale by the time the agent has moved three apps further into a task.

Three things the flag buys, checked against phoneshell's own code

Protection tied to "application under test"What WDA does with itphoneshell's own mechanismGapSource
Quiescence inheritanceA later launch with no explicit setting copies the tracked app's wait-for-idle policyEvery phoneshell launch call sends shouldWaitForQuiescence explicitly (actions.py fallback loop, wda/client.py:380); inheritance is never consultedNone: already overridden on every callwda/client.py launch_app
System alert monitoringWDA's FBAlertsMonitor watches for alerts over the tracked app specificallyAlerts are read from the accessibility tree snapshot on every step regardless of which app is foreground (actions.py:179-187)None: already app-agnosticactions.py snapshot
Crash detectionUnexpected termination of the tracked app raises FBApplicationCrashedException, distinct from a normal state changeapp_state() returns the same code, 1 ("not running"), for a crash, a clean quit, or an app never launched (wda/client.py app_state; actions.py:891)Real: nothing in phoneshell distinguishes a crash from a quiet exitactions.py:891-894

The first two rows are not gaps. phoneshell was built around the assumption, stated directly in open_app's own docstring, that "WebDriverAgent returns success for activate and launch whether or not the app actually comes forward," so it never trusted WDA's bookkeeping for either of those two things to begin with: it polls active_app_info() itself with a 4-second, 0.4-second-interval wait, and it reads alerts out of whatever snapshot it already took. The third row is different. There is no code path, anywhere in actions.py or wda/client.py, that asks whether a now-missing app crashed versus was cleanly terminated versus was never running at all. Those three situations produce the identical string: "not running."

Where a second launch_app call actually happens: 6 of 80 task files 6 74 single-app tasks: one launch_app call, the question never comes up multi.app_switch, multi.three_apps, multi.return_after_detour, resilience.deep_start, resilience.wrong_app, system.spotlight_find_app Each of these 6 calls open_app two or three times in sequence. Every call after the first still goes through the same fallback loop, still never sets asAppUnderTest, so WDA's own tracked "application under test" (if any) is left pointing at whichever app was in front at the last stale-session recovery, not the current one. If one of those apps crashes mid-switch, app_state() reports "not running," the same string a clean quit or a never-launched app produces. Nothing upstream asks WDA's crash-specific signal, because nothing ever claimed the app as tested. Source: environments/*.yaml (80 files, grepped for a second com.apple.* bundle); actions.py open_app, app_state.

What it means if you run a WDA-backed phone agent

Check it yourself

What would prove this wrong

The claim is that phoneshell cannot currently tell a crashed app from a cleanly closed one. Test: launch any installed app with asAppUnderTest: true directly against a phoneshell-managed WDA instance, force-kill it from Springboard, and read /wda/activeAppInfo. If phoneshell's existing code path surfaces a distinct error for that case today, this is wrong. We expect it to report the same "not running" string app_state() already returns for every other kind of absence. We will publish the result, or say we did not run it, by 15 November 2026.

Sources

  1. WebDriverAgent, commit f5c0688, "feat: add asAppUnderTest flag to /wda/apps/launch" (PR #1289), merged into v16.15.0, 12 October 2026
  2. WebDriverAgent CHANGELOG.md, v16.15.0 through v16.13.0 (read 14 October 2026)
  3. phoneshell source: actions.py (open_app, app_state usage), wda/client.py (create_session, launch_app, stale-session recovery), environments/*.yaml (read 14 October 2026)