xcodebuild hangs resolving the simulator destination
xcodebuild stalls while resolving or selecting a simulator destination — sometimes for minutes — before a single test runs.
XCSteward is an OSS macOS tool for predictable XCTest, xcodebuild, simctl, and iOS Simulator execution. Single runs can hang at boot, destination resolution, or readiness; coding agents, scripts, and CI-like workflows sharing one Mac amplify that fragility.
The pain did not start with CI. It got much worse the day coding agents started running iOS tests for me — across more than one app at the same time, on the same Mac.
A single xcodebuild or Simulator run is already fragile on its
own: runs hang before tests start, simctl stops responding,
CoreSimulator wedges. Once several agents, scripts, CI-like jobs, and a
human can all touch Xcode tooling on one machine, those same failures get
frequent and stop being reproducible.
XCSteward is narrow on purpose. It targets operational fragility around local test execution — not your test code.
It does not rewrite your tests or replace xcodebuild. It
governs how runs are executed, isolated, and cleaned up so the
simulator subsystem stays in a state you can reason about.
XCSteward is a local CLI, not a dashboard, SaaS, or MCP layer. The current developer experience has two surfaces: compact human output for watching long runs, and a JSON contract for agents and automation.
Plain submit --wait is no longer a silent black box. It prints
the queued job id, status/log/watch/follow commands, job directory, and
compact wait updates. From another terminal,
status <job-id> --watch [--interval <seconds>]
polls until the job is terminal and logs --follow streams
the combined log until completion.
xcsteward submit --project app --wait --wait-timeout 900
xcsteward status <job-id> --watch
xcsteward logs <job-id> --follow
Machine users should keep using --json. Long-running JSON
waits can add --progress for JSON-lines events on stderr while
stdout stays reserved for the final JSON document. When command events are
available, those progress events add phase and
phase_elapsed_seconds. For streaming status,
status <job-id> --watch --json emits newline-delimited
full JobSummary objects.
xcsteward profile init --detect --json
xcsteward submit --project app --wait --wait-timeout 900 --json --progress --env API_BASE_URL=http://127.0.0.1:8080
xcsteward explain <job-id> --json projects --json and
inspect one with profile show <name> --json.
profile init --detect --json and follow its
next_commands.
submit --metadata key=value and --label.
xcodebuild with repeatable
submit --env KEY=VALUE; CLI overrides beat profile
[env] for that job only, and XCSteward records override
keys rather than sensitive values.
explain <job-id> --json for bounded triage
before opening raw logs; use cleanup --caches for
XCSteward-owned cache and retained evidence cleanup.
doctor as bounded preflight: if
.xctestrun integrity checking times out during a cold or
long build, the warning now says no compiler error was observed before
timeout rather than pretending it diagnosed the build.
If CoreSimulator fails before XCTest attaches, XCSteward reports an
environment failure with the simulator error and artifacts, rather than
making you decide from a silent terminal or raw xcodebuild
output whether the app regressed. The result class
runner_bootstrap_failure means runner or environment setup
failed before XCTest attached; it can come from simulator boot, destination,
launch session, artifact, or runner setup issues.
Signatures such as Unable to boot the Simulator,
launchd failed to respond,
Failed to start launchd_sim,
NSPOSIXErrorDomain code=60,
Failed to prepare device, or testmanagerd connection loss are
classified separately from real test failures. submit --wait,
status, and explain --json preserve the underlying
detail and may suggest a bounded next step, such as shutting down or
erasing the selected simulator before retrying once.
If the test command times out before XCSteward observes XCTest attach or
test-execution evidence, it still stays
runner_bootstrap_failure with
diagnostic_excerpt.subtype = pre_xctest_timeout. Plainly:
the test command hit its timeout before XCSteward observed XCTest attach
evidence. The final summary says XCTest did not attach before the
test command timed out, and terminal JSON can include the phase,
timeout seconds, evidence paths, and a capped diagnostic excerpt.
xcsteward status <job-id> --watch
xcsteward explain <job-id> --json
xcsteward logs <job-id>
When a job is still queued or in simulator/bootstrap setup and the combined
log is not ready, logs <job-id> points back to
status <job-id> --watch instead of turning a pending log
into an opaque missing-file error.
Symptom-first writeups of the failures XCSteward is built around. Each page has quick checks, manual mitigations, and an honest note on fit — useful even if you never install anything. A few to start with, from a library of 18:
xcodebuild stalls while resolving or selecting a simulator destination — sometimes for minutes — before a single test runs.
The simulator boots and reaches the home screen, but the app never launches and testing never begins — the run just sits there.
xcrun simctl commands (list, boot, shutdown, spawn) hang indefinitely and never return — even simple ones like simctl list.
CoreSimulatorService stops responding and the whole simulator subsystem wedges — simctl, Simulator.app, and xcodebuild all block at once.
All tests pass, but fastlane scan never exits — it hangs after the last test during teardown, result-bundle writing, or simulator shutdown.
Coding agents and scripts run xcodebuild/Simulator tests concurrently on one Mac, and runs start colliding, wedging, and failing unpredictably.
Have a real failure mode around xcodebuild, simctl,
Simulator readiness, cleanup, local CI-like workflows, or coding agents
running iOS tests? Try the OSS alpha, or open an issue with your setup.
It is local-first — no signup, no waitlist. The early feedback that helps
most is specific: real logs, real setups, real failure modes, and the
cases where XCSteward does not help.
Install with Homebrew:
# requires macOS with a recent local Xcode
brew tap acyment/tap
brew install xcsteward Dogfooded across 16 real iOS projects and 160 local jobs — each tracked in the public dogfood ledger. It is honest about where it stands: early, alpha-stage OSS, narrow on purpose.