warden

iOS run 2026-10-25 on EAS

The four-framework iOS run of 2026-10-25 at warden 0.3.1, one EAS macos-large worker per framework, 5 runs each, with how it was run, the full tables and every artifact.

The run behind the 2026-10-25 iOS dataset on the comparison page. Everything below comes from the four workers' artifacts, merged into one report; nothing was measured on a developer machine.

Headline

warden ran the warm 8-test suite in 86.30 s. That is 1.56× faster than Argent (134.32 s), 1.06× faster than agent-device (91.61 s, one failed test) and 1.54× faster than tester.army (132.52 s). Per step, warden's median is 85 ms against 321 ms (Argent, 3.8×), 471 ms (agent-device, 5.5×) and 481 ms (tester.army, 5.7×). Its host CPU is 2.97 CPU-s per rep against 4.13 (Argent, 1.4×), 11.18 (agent-device, 3.8×) and 17.74 (tester.army, 6.0×).

How it was run

Workflowapps/example/.eas/workflows/bench-baseline.yml, EAS Workflows run 01a1756a-abaa-7940-95ba-336e7d9160e9, inputs runs=5, frameworks=warden,argent,agent-device,tester-army, max_minutes=55
Codewarden commit 51bb4cee (branch develop, warden 0.3.1), every suite under packages/e2e-bench/suites/
WorkersOne EAS macos-large worker per framework, four in all. VM template macos-tahoe-26.5-xcode-26.6: VirtualMac2,1, Apple M4 Pro (Virtual), 10 cores, 40 GB, macOS 26.5.2 (25F84), Xcode 26.6 (17F113)
SimulatorEach worker claims its own headless simulator with warden claim ios --headless (iPhone 17, iOS 26.5, named warden-iphone-17-1), created fresh for the job and shut down at the end. Nothing one framework leaves behind can reach another
AppThe same build on every worker: nz.co.delacour.warden.example, EAS bench profile (Release, simulator), sha256 c283eae41c7a, 101.1 MB, reused by fingerprint (build_ios skipped), installed with simctl install before the bench
Runs5 runs of the 8-test suite per framework. Run 1 is cold; warm numbers are the median of runs 2 to 5. retries=0, fresh app launch per test, caffeinate -dims held
Before the benchbun install, warden-simd built from source (Swift release), the framework installed (bun add -g @swmansion/argent@0.30.1 / agent-device@0.21.31; tester.army's e2e 0.20.0 comes with bun install, Node 22.23.1), then the job waits for the 1-minute load average to drop under the core count and records a 60 s idle control of the simulator with the app in the foreground
Deep-link sheetiOS shows "Open in …?" for the first custom-scheme link after a boot and never again, so the harness opens one link and answers the sheet before every framework's prepare. No suite handles the sheet itself
Hard stopA watchdog stops the bench at max_minutes; it did not fire on any worker (the longest job, agent-device, ran 28 min)
Isolation per frameworkHelpers of every framework stopped, driver apps uninstalled before the run (cold cost measured), residue listed after teardown
SamplingHost and simulator processes sampled every 500 ms from ps; the CPU headline is CPU-seconds of the framework's own process tree during the timed run, warm median
Evidence on exitEach job screenshots the simulator and lists the apps still running, releases the lease even when a step failed, and uploads an eas-ios-<framework> artifact
Versionsbun 1.3.14, warden 0.3.1, Argent 0.30.1, agent-device 0.21.31, tester.army e2e 0.20.0 (bundled agent-device 0.21.25), Node 22.23.1

Hosted workers idle hotter than a Mac and share their host, so these numbers compare only with other macos-large runs, never with runs on other hardware.

agent-device dropped one test in run 1 (sign-in/field-errors: its fill read back an empty field after typing), so its pass rate is 97.5% and, by the fairness rules, it cannot be called fastest even where a per-test median is lower. Its other 39 tests passed.

Results

Summary

FrameworkStatusVersionSuite median (warm)p95RatioCold rep 1Cold-start costPrepareTeardownOverheadPass rateFlakeResidueFootprint (LOC / deps)
wardenokbun 1.3.14 + warden-e2e86.30 s86.71 s1.00×71.98 snone (rep 1 not slower, -13.72 s)596 ms72 ms67 ms100%0%0 proc / 0 app / 0 path379 LOC / 0 B
argentok0.30.1134.32 s134.65 s1.56×133.61 s1.01 s1.72 s293 ms163 ms100%0%0 proc / 0 app / 1 path161 LOC / 0 B
agent-deviceokagent-device 0.21.3191.61 s ✗91.70 s1.06×87.46 s12.23 s16.37 s315 ms213 ms97.5%2.5%0 proc / 1 app / 1 path158 LOC / 0 B
tester-armyoke2e 0.20.0132.52 s132.89 s1.54×150.63 s18.11 s0 ms495 ms2.24 s100%0%0 proc / 1 app / 1 path307 LOC / 0 B

Per-test medians (ms)

Testwardenargentagent-devicetester-army
onboarding steps through to sign-in2929 (1.00×)8301 (2.83×)3830 (1.31×)5964 (2.04×)
invalid email and short password show field errors16054 (1.00×)18221 (1.13×)16579 ✗ (1.03×)21119 (1.32×)
a locked account shows the form alert10096 (1.04×)14442 (1.49×)9673 (1.00×)12914 (1.34×)
valid credentials land on todos9788 (1.00×)15416 (1.58×)9784 (1.00×)14850 (1.52×)
add, complete and filter todos14494 (1.00×)21110 (1.46×)15470 (1.07×)21230 (1.46×)
open a todo, mark it done, then delete it11089 (1.00×)19105 (1.72×)12422 (1.12×)18121 (1.63×)
deep link opens a todo by id10656 (1.02×)17811 (1.70×)10483 (1.00×)18094 (1.73×)
settings shows the account, toggles switches and signs out10945 (1.00×)19616 (1.79×)13136 (1.20×)18075 (1.65×)

Per-step latency

FrameworkStepsMedianp95Max
warden50485 ms3.63 s4.98 s
argent1192321 ms1.82 s2.82 s
agent-device632471 ms1.62 s1.89 s
tester-army592481 ms3.21 s4.01 s

Where step time goes

Step scopes differ per tool: warden's one fill step spans the focus tap, select-all, typing and read-back confirm, argent reports a separate idle step and into …, agent-device reports fill and type separately, and tester.army reports locator steps. Compare a verb's share within a tool, not its median across tools.

warden

VerbnShareMedianp95Max
fill6880.4%3.38 s4.20 s4.98 s
tap18010.6%75 ms333 ms620 ms
expect2569.1%21 ms558 ms578 ms

argent

VerbnShareMedianp95Max
into6827.9%2.15 s2.81 s2.82 s
idle23227.5%925 ms1.04 s1.24 s
visible32018.3%13 ms1.03 s1.04 s
id=38012.8%322 ms352 ms372 ms
launch3210.9%1.80 s1.82 s1.83 s
hidden322.4%16 ms1.05 s1.05 s
open-url40.1%116 ms124 ms124 ms
next-test.yaml280%4 ms6 ms6 ms
other960%1 ms5 ms6 ms

agent-device

VerbnShareMedianp95Max
press24440.6%520 ms1.02 s1.09 s
type6423.9%1.26 s1.86 s1.89 s
wait27620.9%55 ms969 ms1.08 s
open369%1.02 s1.07 s1.10 s
fill125.5%1.69 s1.75 s1.75 s

tester-army

VerbnShareMedianp95Max
getbytestid49669.5%267 ms3.23 s4.01 s
dismiss5222.3%2.26 s2.33 s2.33 s
(unlabelled)326.3%1.03 s1.05 s1.06 s
accept41.6%2.02 s2.03 s2.03 s
warden-example40.2%290 ms295 ms295 ms
getbytext40.1%65 ms66 ms66 ms

Untraced time

FrameworkUntraced / repShare of test timeLargest (test)
warden3.17 s3.7%todo-detail/deep-link (716 ms)

Test wall time that no step and no span of the framework's own trace covers (warden: trace.jsonl; nested steps and spans count once). Runner overhead, hooks, sleeps and calls nobody wrapped end up here.

Resources

FrameworkScopeCPU-s / repCPU-s (whole)CPU avgCPU peakRSS avgRSS peak
wardenhost2.97 s15.36 s3.0%38.8%145.9 MB158.6 MB
wardensim—329.55 s74.3%282.8%10.6 GB10.7 GB
argenthost4.13 s21.38 s2.7%41.8%181.4 MB236.7 MB
argentsim—325.71 s44.5%209.9%10.5 GB10.6 GB
agent-devicehost11.18 s78.73 s13.6%611.5%711.9 MB2.4 GB
agent-devicesim—433.50 s86.0%248.7%10.8 GB11.0 GB
tester-armyhost17.74 s110.24 s13.8%611.2%598.2 MB2.5 GB
tester-armysim—619.50 s84.1%248.1%11.1 GB11.4 GB

Sampled every 500 ms from ps. host = the framework's own process tree; sim = every process of the simulator's launchd_sim tree, or on Android the app under test plus every framework's on-device helpers from top (every 2 s). RSS is a per-process sum, so shared framework pages are counted once per process: compare the numbers across frameworks, not against physical memory.

CPU-s / rep (the CPU headline) is the CPU time of the timed run only, median over the warm reps (over every rep, cold included, when a framework has no warm rep, as with --runs 1), from per-process deltas of ps time; the other columns are whole-framework (prepare, every rep, the per-rep housekeeping and teardown), so their averages move with suite length. CPU avg / CPU peak are ps %cpu, a decaying average on macOS, kept for comparison with older runs. Android sim has no CPU-seconds (top only). CPU-seconds undercount: CPU a process uses after the last 500 ms tick, and processes living between ticks, are only recovered for children the bench ran itself (their rusage at exit); daemons and processes a framework spawns and never waits for are not, so frameworks that fan out to many short-lived tools read low. An exit charge is booked to its root's group, so in the per-group split the unseen CPU of a descendant of another group (a bun test under a warden root) shows under the root. procps (Linux) ps time has 1 s resolution.

Host by process group

FrameworkGroupCPU-s (whole)RSS avgRSS peak
wardenbun-test6.21 s89.6 MB102.2 MB
wardenwarden-simd8.61 s56.2 MB56.7 MB
wardenwarden-cli0.11 s131.6 KB78.8 MB
wardenother0.43 s0 B0 B
argentother21.38 s181.4 MB236.7 MB
agent-deviceother78.73 s711.9 MB2.4 GB
tester-armyother110.24 s598.2 MB2.5 GB

bun-test the spec process, warden-simd the resident iOS helper, warden-cli the warden commands the bench spawned, adb / emulator the Android tools, other the rest (another framework's runner, xcrun, java). Each group's RSS average counts 0 for ticks without a process, so the groups add up to the host.

Where the CPU goes

warden

ProcessScopeCPU-sShareRSS avgRSS peakSpawns
warden-simdhost1.76 s60.0%56.5 MB56.7 MB1
bun testhost1.16 s39.5%90.1 MB101.3 MB1
bun test (exit charge)host0.01 s0.5%0 B0 B0
bun cli.tshost0.00 s0.0%0 B0 B0
simctlhost0.00 s0.0%0 B0 B0
wardenexamplesim32.07 s48.8%373.1 MB428.9 MB8
iOSsim25.41 s38.6%9.7 GB9.7 GB108
SimulatorFrameworkBridge-iOS servesim3.53 s5.4%185.6 MB199.8 MB13
bun cli.tssim2.28 s3.5%228.3 MB229.5 MB1
CoreSimulatorBridgesim1.25 s1.9%84.9 MB85.0 MB1
pssim0.69 s1.0%1.8 MB1.8 MB171
launchd_simsim0.33 s0.5%36.8 MB36.8 MB1
xpcproxy_simsim0.22 s0.3%411.4 KB13.3 MB7
pkdsim0.00 s0.0%58.9 MB58.9 MB1
aslmanagersim0.00 s0.0%21.2 MB21.2 MB1
caffeinatesim0.00 s0.0%7.3 MB7.3 MB1
(launchctl)sim0.00 s0.0%0 B0 B0
othersim0.00 s0.0%0 B0 B0

argent

ProcessScopeCPU-sShareRSS avgRSS peakSpawns
simulator-serverhost3.92 s95.6%96.0 MB96.0 MB1
node argenthost0.18 s4.3%85.7 MB88.1 MB1
node argent (exit charge)host0.00 s0.1%0 B0 B0
(simctl)host0.00 s0.0%0 B0 B0
node tool-server.cjshost0.00 s0.0%0 B0 B0
simctlhost0.00 s0.0%0 B0 B0
system_profilerhost0.00 s0.0%0 B0 B0
wardenexamplesim33.67 s52.9%374.7 MB434.8 MB8
iOSsim24.31 s38.2%9.7 GB9.7 GB106
bun cli.tssim3.34 s5.2%225.2 MB229.3 MB1
pssim1.07 s1.7%1.8 MB1.8 MB267
CoreSimulatorBridgesim0.48 s0.8%117.8 MB118.2 MB1
simctlsim0.41 s0.6%351.9 KB20.5 MB5
launchd_simsim0.21 s0.3%36.9 MB36.9 MB1
xpcproxy_simsim0.15 s0.2%155.7 KB12.1 MB4
(wardenexample)sim0.04 s0.1%1.5 MB395.3 MB1
pkdsim0.00 s0.0%59.0 MB59.0 MB1
aslmanagersim0.00 s0.0%21.1 MB21.1 MB1
caffeinatesim0.00 s0.0%7.2 MB7.2 MB1
othersim0.00 s0.0%0 B0 B0

agent-device

ProcessScopeCPU-sShareRSS avgRSS peakSpawns
xcodebuildhost7.97 s71.9%219.6 MB220.4 MB1
bun cli.tshost1.93 s17.4%239.8 MB245.3 MB1
snapshot-bridgehost0.78 s7.0%116.6 MB116.7 MB1
pshost0.22 s2.0%1.8 MB1.9 MB181
node agent-devicehost0.14 s1.3%61.1 MB66.7 MB1
simctlhost0.04 s0.4%20.3 MB20.3 MB1
node agent-device (exit charge)host0.01 s0.1%0 B0 B0
DTServiceHubhost0.00 s0.0%16.4 MB16.4 MB1
caffeinatehost0.00 s0.0%7.1 MB7.1 MB1
(clang)host0.00 s0.0%0 B0 B0
(ps)host0.00 s0.0%0 B0 B0
(simctl)host0.00 s0.0%0 B0 B0
otherhost0.00 s0.0%0 B0 B0
iOSsim37.64 s44.2%10.1 GB10.1 GB110
wardenexamplesim36.00 s42.3%375.2 MB438.6 MB8
AgentDeviceRunnerUITests-Runnersim10.00 s11.7%269.7 MB269.8 MB1
simctlsim0.55 s0.6%749.3 KB20.7 MB8
CoreSimulatorBridgesim0.54 s0.6%81.6 MB81.7 MB1
launchd_simsim0.21 s0.2%37.0 MB37.1 MB1
xpcproxy_simsim0.16 s0.2%257.5 KB12.7 MB6
launchctlsim0.08 s0.1%24.8 KB4.4 MB1
pkdsim0.00 s0.0%59.1 MB59.1 MB1
aslmanagersim0.00 s0.0%21.2 MB21.2 MB1
(launchctl)sim0.00 s0.0%0 B0 B0
(managedappdistri)sim0.00 s0.0%0 B0 B0
othersim0.00 s0.0%0 B0 B0

tester-army

ProcessScopeCPU-sShareRSS avgRSS peakSpawns
xcodebuildhost14.70 s83.0%221.6 MB223.0 MB1
node entry.jshost1.18 s6.7%85.2 MB94.4 MB1
node e2e (exit charge)host0.76 s4.3%0 B0 B0
snapshot-bridgehost0.62 s3.5%116.4 MB116.6 MB1
node e2ehost0.43 s2.4%104.8 MB134.0 MB1
other (exit charge)host0.03 s0.2%0 B0 B0
simctlhost0.00 s0.0%20.5 MB20.5 MB1
DTServiceHubhost0.00 s0.0%16.8 MB16.8 MB1
(ps)host0.00 s0.0%5.0 KB1.3 MB1
(clang)host0.00 s0.0%0 B0 B0
(pgrep)host0.00 s0.0%0 B0 B0
(plutil)host0.00 s0.0%0 B0 B0
otherhost0.00 s0.0%0 B0 B0
wardenexamplesim50.77 s41.4%381.6 MB441.3 MB9
iOSsim50.70 s41.4%10.1 GB10.1 GB112
AgentDeviceRunnerUITests-Runnersim16.40 s13.4%271.0 MB271.1 MB1
bun cli.tssim2.46 s2.0%264.5 MB270.9 MB1
CoreSimulatorBridgesim0.62 s0.5%81.5 MB81.6 MB1
simctlsim0.61 s0.5%563.5 KB20.7 MB9
launchd_simsim0.42 s0.3%37.7 MB37.7 MB1
(launchctl)sim0.14 s0.1%62.1 KB15.9 MB1
pssim0.14 s0.1%1.8 MB1.8 MB262
(managedappdistri)sim0.13 s0.1%68.6 KB17.6 MB1
xpcproxy_simsim0.11 s0.1%119.0 KB13.4 MB4
launchctlsim0.07 s0.1%10.4 KB2.7 MB1
othersim0.00 s0.0%87.2 MB87.2 MB3

One table per framework, from the warm-median rep's timed run (the rep whose host CPU-seconds is the lower median). Rows are processes grouped by label (top 12 per scope, the rest in other); host is the framework's own process tree, sim the simulator's launchd_sim tree (iOS only). Share is of the scope's CPU-seconds. RSS is the label's summed RSS per 500 ms tick (0 where it had no process). Spawns counts distinct pids a tick saw: a process that lives between two ticks is only seen through the (exit charge) rows (CPU of a child the bench ran, booked at its exit), so many short-lived xcrun simctl calls read low in Spawns and in CPU.

Residue

  • warden: none
  • argent: paths: /Users/expo/.argent (328.0 KB)
  • agent-device: driver apps: com.callstack.agentdevice.runner.uitests.xctrunner; paths: /Users/expo/.agent-device-bench/agent-device (2.9 MB)
  • tester-army: driver apps: com.callstack.agentdevice.runner.uitests.xctrunner; paths: /Users/expo/.agent-device-bench/tester-army (4.9 MB)

Unsupported / failed

None.

Change since the previous run

The previous four-framework run on the same workflow and worker class, 01a172f5 at warden 0.1.1, is replaced by this one. Between them warden's resident helper took over launch, terminate and open-URL from simctl, reads fewer trees after an input, links idb 1.7.7 with its single-pass frame decoder, and boots simulators slim. The other frameworks are at the same versions on both runs.

warden, warm0.1.1 (01a172f5)0.3.1 (01a1756a)Delta
Suite median97.04 s86.30 s-10.74 s (-11 %)
Step median170 ms85 ms-85 ms (-50 %)
Host CPU-seconds per rep9.12 s2.97 s-6.15 s (-67 %)
of which warden-simd7.03 s1.76 s-5.27 s (-75 %)
Host RSS peak184.9 MB158.6 MB-26.3 MB
Ratio to Argent1.36×1.56×
Ratio to agent-device0.95×1.06×
Ratio to tester.army1.36×1.54×
Pass rate / flakes100 % / 0100 % / 0

Argent, agent-device and tester.army moved by less than 2 % between the two runs (131.81 → 134.32 s, 91.77 → 91.61 s, 132.34 → 132.52 s), which is the worker-to-worker noise to read every ratio against.

Artifacts

Each worker uploaded its results directory as one archive. The viewable files are copied here; the archives hold everything, including the per-rep resources.ndjson ticks and the 60 s idle control. To re-render one: download the archive, tar -xzf it, then bun run bench report Users/expo/bench-results from the warden repo.

Merged run

The four results merged into one run, bench-2026-10-25-ios-eas, ratios recomputed across the four: report.html, report.md, bench-results.json, summary.json (the comparison page's dataset).

warden

Worker job bench_warden, bun 1.3.14 + warden-e2e (this commit). Download the whole artifact (the EAS eas-ios-warden archive, unpacks to Users/expo/bench-results).

The warden worker's simulator at exit

Argent

Worker job bench_argent, @swmansion/argent 0.30.1. Download the whole artifact (the EAS eas-ios-argent archive, unpacks to Users/expo/bench-results).

The Argent worker's simulator at exit

agent-device

Worker job bench_agent_device, agent-device 0.21.31. Download the whole artifact (the EAS eas-ios-agent-device archive, unpacks to Users/expo/bench-results).

The agent-device worker's simulator at exit

tester.army

Worker job bench_tester_army, e2e 0.20.0, bundling agent-device 0.21.25. Download the whole artifact (the EAS eas-ios-tester-army archive, unpacks to Users/expo/bench-results).

The tester.army worker's simulator at exit

On this page