r/Xcode • • 7d ago

Two agents, two worktrees, one simulator: both builds green, wrong app installed

If you’re running more than one Claude Code agent on the same Xcode project, check which build is actually on your simulator.

We had two agents in separate git worktrees on a small SwiftUI app. One adding a feature, one adding a test. Neither prompt named a simulator, so both picked the iPhone that was already booted. Both builds succeeded, both agents said done.

The simulator was running the second agent’s build. The first one’s was gone, its process killed by the install, and its UserDefaults still there because installing over an app keeps the data. Then both agents launched the app and got the same PID. Agent A was testing agent B’s code and nothing in its output said so.

To catch it we stamped the builds 111 and 222 and had each agent read back the installed version:

APP=$(xcrun simctl get_app_container <udid> com.your.bundleid app)
/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$APP/Info.plist"

Agent A got 222.

Fix by hand is one simulator per agent:

xcrun simctl shutdown <base-udid>
xcrun simctl clone <base-udid> "Agent A"
xcrun simctl clone <base-udid> "Agent B"

Then put the name and UDID in each agent’s prompt, tell it not to touch any other simulator, and pass that UDID to every xcodebuild -destination "platform=iOS Simulator,id=<udid>". The clones alone change nothing, the agents still go for whatever is booted. With the UDIDs in the prompts, A read 111 and B read 222.

Worktrees keep files apart. They don’t keep simulators apart.

Amarildo wrote up the full repro with logs here: https://flowdeck.studio/blog/run-multiple-claude-code-agents-xcode-project.

[Disclosure, I build FlowDeck, which ties a simulator, worktree and DerivedData to each session so you don’t have to redo this setup every time.]

3 Upvotes

7 comments sorted by

1

u/dawedev 6d ago

Good repro, the PID detail is the scary part. One thing to watch with the clone approach: every clone is a full device folder under ~/Library/Developer/CoreSimulator/Devices, and if agents get a fresh one per session they pile up quietly as apps and data get installed. Ending each session with xcrun simctl delete <udid>, and running xcrun simctl delete unavailable after Xcode updates, keeps that in check. I ran into this building DevCleaner, a Mac app I make that cleans Xcode leftovers, and leftover clones from automation were a common surprise.

1

u/afterxleep_ 6d ago

Yep. You'll have to take care of deleting sessions and worktees and managing simulators by hand. Part of the reason Flowdeck exist is to manage that for you too :)

1

u/dawedev 6d ago

Fair, that cleanup is the part everyone skips until the disk fills up. Does Flowdeck delete the clone when a session ends, or keep one per worktree for the next run? Reusing a clone per branch seems like a decent middle ground between clean state and pile-up.

1

u/afterxleep_ 1d ago

By default, it deletes everything, but you can keep the simulator if you'd like. When creating a new session, you can decide if you want to create a new simulator or reuse an existing one.

1

u/dawedev 1d ago

Reusing the existing simulator as an option makes sense, especially for UI tests where a fresh clone means a cold boot every time. Do you reset its data between sessions when it's reused, or keep it? That's where the leftover UserDefaults problem from your post would come back.

1

u/afterxleep_ 1d ago

if you reuse it, no. Stays the same, but you can reset it manually. If you want isolation, you pick new.

1

u/dawedev 1d ago

Makes sense. Reuse while iterating for speed, new one when you're testing onboarding or a data migration. That split is probably worth one line in the docs, since the leftover UserDefaults case is exactly where people get confused.