fix(e2e): launch a device's pinned app before explore and MCP look at it

Merged
KR
8d ago
+94-17

On a device target, e2e explore and MCP open_session took their first look at the screen without ever opening the app. They depended on the mobile engine's warm-up to have opened it, and the warm-up skips the open when the target names an app.appPath. The result was APP_NOT_OPEN: iOS snapshot requires an active app session, and MCP returned no session id (ENG-865, two e2e feedback reports).

  • launchesPinnedApp(target) (packages/e2e/src/config/targets.ts) says when app.open() launches a pinned app rather than opening a URL: no app URL, and an engine with no open that relaunches the app in restart.
  • Explore now opens the app on such a target too (packages/e2e/src/explore/index.ts), so its first step is app.open, as on the web.
  • MCP calls attempt.fixtures.app.open() for such a target before the session's first observation (packages/e2e/src/mcp/session.ts). It is recorded as an ordinary app.open step, and the opening text names the app (App: com.example.app, launched fresh.), as it names the URL on the web.
  • The app now launches fresh, the way a test's app.open() launches it, instead of opening on whatever screen the warm-up left.
  • On a local device, a target with only app.appPath still has nothing to launch in explore or MCP, since only a test's device.installApp() installs the build. It now fails naming the missing install instead of APP_NOT_OPEN.
  • The fake engine gets a device mode (no open, restart opens the app), used by one new explore test and one new MCP test. Both fail on main.

Verified

Ran it locally: yes

  • iOS simulator (iPhone 17 Pro) on a Mac, the built CLI from main and from this branch. The pinned app is Settings: app: { bundleId: 'com.apple.Preferences', appPath: './missing.app' }, so the warm-up skips the open, as for an Expo project that names its build.
  • MCP: a stdio client calls open_session. Explore: e2e explore "Look around Settings" --max-steps 1 with a deliberately invalid AI Gateway key. Explore looks at the screen before its first model call, so an unauthorized model call means the screen was readable.
mainthis branch
MCP open_sessionAPP_NOT_OPEN: snapshot failed: iOS snapshot requires an active app session on the target device., no sessionSession … open on target "ios", first observation returned
explorestep 1 agent.extract fails with APP_NOT_OPENstep 1 app.open (2.4s), then agent.extract reaches the model at com.apple.Preferences / Settings

With bundleId alone both builds open the session, since the warm-up opens the app. This branch adds the app.open step there as well. Not run: Android, and an exploration with a working model.


Made with Claude Code (Opus 5.5).