Repository navigation
Description
Seen in 0.21.15, macOS, iOS simulators.
Problem
Our dev scripts install and relaunch a dev client on an iOS simulator with xcrun simctl install and xcrun simctl launch --terminate-running-process. Before each call, they read the claim file in ~/.agent-device/device-claims/ and refuse when another session holds the device.
Between that check and the simctl call, an agent-device client (an e2e run, or an IDE device panel) can take the claim. The install or relaunch then interrupts that session. The window is short, but a check that only our scripts honor cannot close it. The claim must come from agent-device.
What exists in 0.21.15, and why it does not fit
device status|releaseonly inspects or releases claims. No command takes one.install,bootandshutdowntake atransient-exclusiveclaim, but only for their own work. agent-device cannot run oursimctlcommand under that claim.open --session S --platform ios --udid Uwith no app holds a session claim untilclose --session S. As a reservation, it has these side effects:- it starts a daemon in the state dir;
keepHotcan startxcodebuild build-for-testingfor the runner in the background;- on a simulator that is not booted, it boots it and runs
open -a Simulator; - if the script crashes before
close, the claim stays held while the daemon lives.
takeoverneeds a remote lease, so it does not apply to a local simulator.
Proposal (any one is enough)
agent-device exec --platform ios --udid U -- <command...>: take atransient-exclusiveclaim, run the child, and release the claim when the child exits. The claim owner is the exec process, so a crash of the child or of agent-device frees the claim.agent-device device claim --udid Uanddevice release --udid U --session S: take a claim whose owner is the calling process (or one with a TTL or heartbeat). No daemon, no boot, no runner prewarm, no session record.open --claim-only: the currentopenwith no app, but without the boot,open -a Simulatorand runner prewarm.
Option 1 fits our use best. The claim lasts exactly as long as the mutation, and nothing leaks.
Current workaround
We read the claim file right before each simctl call. That narrows the window but does not close it.
Related: #2161 (workspace-scoped device pinning).
Activity
PrinceD96 commentedon Oct 8, 2026
Same need from an Expo project. Parallel agents in separate worktrees build with
expo run:ios --device <UDID>, which installs and launches throughsimctl, so the install never meets a claim and can replace another session's app.Option 1 would let us run:
and the install would be fenced off without a wrapper script.
Our current workaround is to build without a device (
expo run:ios --device generic --output <dir>) and install withagent-device reinstall. That only works across daemons, though; see #3345.thymikee commentedon Oct 9, 2026
Decision: accepted in direction, as a process-owned claim wrapper (option 1, an
exec-style command). It's separate from #2161's project pinning.Design constraints before implementation:
thymikee commentedon Oct 16, 2026
An agent is picking this up; the PR will link here.
thymikee commentedon Nov 2, 2026
Rescoped to documentation, since #3738 was closed in favor of a recipe. Scope: one Sessions recipe that acquires a uniquely named, explicit-UDID session with an app-less
open, runs the external command only after the claim succeeds, keeps its exit status, and closes the session in shell cleanup. It also states the boot or prewarm side effects and crash recovery, and that this is not a process-owned claim.docs(sessions): add a recipe to hold a device claim around an externa…
docs: new-package publish rule, ADR scope, device-claim recipe, find …