Parallelise your agent workflows and e2e tests.
warden leases every simulator, emulator and port on your Mac to one agent, worktree or run at a time, then fans your e2e suite across as many simulators as you give it. No two runs ever drive the same device.
$ npm i -g @delacour/warden && warden installnpm release pending. Build from source until then.
warden batch salient-e2e37 Salient e2e flows · 5 iPhone 17 simulators · 3m25s (13m55s on one) · 37/37 passed · shown at 5× speedYour whole suite, split across every simulator.
Salient's 37 e2e flows take 13m55s on one simulator and 3m25s on five. A batch leases the simulators, starts your serve step once, and hands the next flow to whichever simulator frees up first. Declare it once in warden.config.json; every agent and human runs the same command.
Lease
N simulators and every port the run needs, all-or-nothing, released on exit.
--countServe
Metro, an API, a seeded database: started once, and jobs wait until it is ready.
--serveQueue
One worker per simulator pulls the next flow, so a slow flow never holds up the rest.
{job}Retry
A failed flow reruns on the same simulator, with its own log.
--retryRecord
Every simulator on video, the live grid as a cast, and a timeline of every job.
--record
// warden.config.json
"batches": {
"salient-e2e": {
"project": "salient",
"platform": "ios",
"count": 5,
"profile": "iphone-17",
"app": true,
"retry": 1,
"serve": "bun scripts/e2e/run-ios.ts --session",
"serveReady": "file:e2e-artifacts/batch/session.json",
"jobsFrom": { "command": "bun scripts/e2e/select-flows.ts --list" },
"cmd": ["bun", "scripts/e2e/run-ios.ts", "--attach", "{job}", "--device", "{udid}"]
}
}$ warden batch salient-e2eNew simulators in sixteen seconds, not four minutes.
Five simulators in parallel means five devices to boot. A new simulator's first boot spends most of its time on a one-time data migration; warden does that once, keeps the result as a golden image, and clones every new pool device from it.
Fresh create + first boot
simctl create, then the one-time data migration
146–239 s
Clone a settled golden + boot
clone 3.7 s + boot 11.9 s, what claim does for a new sim
15.6 s
3 clones booted in parallel
three new sims at once
~20 s
Reuse an existing pool device
second boot; warden always reuses first
~6.5 s
simctl create.Parallel, never shared.
Every claim runs in one sqlite transaction on this machine, so two agents asking at the same instant are served in turn, never both.
Claim
Reuse a free booted warden sim, else clone one, up to --max per profile.
claimStay alive
A lease lives while its pid lives or its heartbeat is under 30 min old.
heartbeatRelease
Only sims warden created, or you booted, are shut down on release.
releaseReclaim
Dead sessions' leases go stale and their sims are shut down. Never deleted.
gc
Touch a device it didn't create
Your own Simulator.app or another tool's sim is read-only unless you pass --adopt.
Hand one device to two owners
warden check exits 2 when another owner holds a device.
Give out a port that's in use
Ports are bind-probed and leased from a range, like devices.
Boot a golden image
Goldens are never allocated, booted by gc, or counted in the pool.
Your agents claim before they tap.
warden install adds two hooks to Claude Code and Codex. An agent's first simulator tool call claims a device for its session; a call on someone else's device is blocked.
PreToolUse
Auto-claims an unleased device, heartbeats your own, blocks another owner's with their repo and worktree.
SessionEnd
Shuts down the sims the session booted and releases its leases. /clear keeps them running.
warden run
Claims devices and ports, exports them to your script, releases on exit.
$ warden run ios --count 2 --port 8091:20 -- bun run e2e WARDEN_UDIDS=8F3A2C1E-…,41D09B7A-… WARDEN_PORT_0=8093 # heartbeat every 30 s · SIGINT forwarded · released on exit
Codex skips new hooks until you trust them: run /hooks once after installing.
Install
Install the CLI, wire the agent hooks, check the setup. Every agent-config change shows a diff and asks first. Until the npm release lands, build from a checkout.
1Install warden
$ npm i -g @delacour/warden2Hook up your agents
$ warden install3Check it
$ warden doctor