Read this for Apple runner changes or manual agent-device runs on simulators, emulators, or
physical devices. Live verification steps apply when exercising a device-facing path.
- After changing runtime code reached through
bin/agent-device.mjsor the daemon:pnpm build, thenpnpm clean:daemon— the daemon does not self-reload. - Before any Android verification from source:
pnpm build,pnpm build:android,pnpm clean:daemon.build:androidrefreshes and verifies both bundled Android helper artifacts for the current package version. shutdownhands off a healthy simulator runner; a new daemon may adopt the old binary. After Swift runner changes, runpnpm build:xcuitestbefore verification. Use the session cleanup procedure below if ownership is stuck.
- Android: capture
snapshot -i --jsonand requireandroidSnapshot.backendto beandroid-helperwithhelperVersionequal topackage.json's version. A stock UIAutomator fallback is not valid verification unless the fallback itself is the behavior under test. - For repo-owned
Agent Device Testerwork,examples/test-app/README.mdis the source of truth for simulator, physical-device, Metro/dev-client, and app-surface steps. An already-installedcom.callstack.agentdevicelabis not sufficient — the README's Metro/dev-build andsnapshot -ichecks must prove the expected app surface is running. - For Android RN/Expo/dev-client apps that use local Metro, configure
adb reverse tcp:<port> tcp:<port>for the app's Metro port before opening the app or URL.
- Source-checkout daemon state is worktree-scoped, but devices are not. Use
pnpm daemon:state-dirto inspect it and different devices for concurrent worktrees. - The first Node process after a newly signed Apple runner launches may block during Gatekeeper
verification. Warm it with a throwaway
node -e 0before measuring. DEVICE_IN_USEhas two flavors. "already in use by session X" is this daemon — follow itsclose --sessionhint. "owned by session X in workspace Y" is another worktree's device claim — non-retriable; run the error'sdevice status/device release --stalerecovery, never PID hunting.
The OS-neutral Apple runner lives under packages/platform-apple/src/runner/. For connection errors,
retry policy, or command typing, start at runner-contract.ts; transport stays below session/client
behavior, and xctestrun build/cache logic stays outside request execution.
- Close manually opened sessions, including failed verification attempts, using their original
--session,--platform,--udid, and--state-dirvalues. - Use a purpose-specific session name for experiments, and an isolated
--state-dirunder/private/tmpwhen you need cleanup isolation beyond the current worktree's default daemon. - If
closeis blocked or ownership looks stuck, inspect it withagent-device device status --stale(daemonless), stop the owning daemon withagent-device daemon stop --state-dir <dir>(add--cleanto remove retained runners), and release provably dead owners withagent-device device release --stale. Do not hunt PIDs withps/kill. - If cleanup cannot be completed, report the remaining session name, state dir, and the
device status --staleoutput as a blocker.
The daemon binds localhost. If the sandbox rejects the listener with listen EPERM, rerun with
host access when permitted. Generic Failed to start daemon or cleanup errors alone do not prove a
sandbox cause; inspect the underlying failure. Run other checks in the sandbox unless their tools
require host access.