A practical way to use Git worktrees and AI coding agents to build, test, review, and merge macOS app changes in parallel.
A button-color change may take an agent five minutes. Preparing an environment where it can build, launch, test, and prove that change may take much longer.
Git worktrees solve file conflicts. Several agents can edit separate branches without overwriting one another’s files. They do not isolate the rest of the Mac. Builds still share machine capacity. Debug apps can share identity and data. Tests can reach the wrong running copy. Local services can collide on ports, databases, and containers.
We hit all of these problems while developing a production Mac app with multiple agents. Bigger prompts did not help. We fixed them by putting a repeatable coding-agent harness around each task. That is what this article means by a software factory.
What Git worktrees leave shared
Git worktrees let one repository keep several branches checked out in separate directories. One agent can work on settings while another changes onboarding, with neither switching the shared checkout.
A new worktree is still only a directory. Setup has to make it a runnable environment. Separate worktrees may continue to share the same:
- Xcode installation, CPU, memory, package caches, and simulator.
- DerivedData, bundle identifier, app container, preferences, and Keychain.
- Ports, local databases, Docker resources, helpers, and backend accounts.
- Copied environment files, generated configuration, logs, and test evidence.
- Continuous-integration rules, pull-request target, and merge queue.
Parallel agent coding is a systems problem. Git is only one part of it. Tasks can run in parallel on a Mac when their source, runtime, services, verification, and cleanup boundaries are clear.
The worktree should belong to the task rather than to a particular agent. One agent may implement the change while another reviews or continues it. If two agents write in the same worktree, their file ownership must be explicit and must not overlap.
The software factory has three jobs
- Give each change its own branch, worktree, build output, running app, local services, data, evidence, and cleanup path.
- Encode setup, build, run, test, and cleanup in deterministic repository commands. The app must accept task-specific configuration.
- Let the agent prove the exact build in a real runtime, then give the reviewer a short path to launch and inspect it.
A “done” message is a status update. Another person still needs to inspect the change, run the exact build, and see the evidence.
1. Isolate each task
Source and build output
Each task needs an
isolated coding-agent workspace
with its own branch and worktree. Put build output in a DerivedData directory
tied to that worktree. Agents should use the repository’s supported build
command rather than assembling their own xcodebuild flags.
Separate build directories prevent file collisions, but they do not create more machine capacity. Heavy builds still compete for CPU, memory, disk bandwidth, and thermal headroom. A small queue that names the active build and links to its log makes that contention visible instead of making a healthy task look stuck.
The running app and its services
Two successful builds can still launch as the same app. Apple treats the bundle identifier as the system-wide identity of a bundle, and other capabilities may depend on it.
For a straightforward project, each debug app can use a worktree-specific bundle suffix and its own app-support directory. Its database, preferences, caches, logs, and temporary files should be separate from other tasks. The test harness must target the app built from the current worktree rather than another debug copy that happens to be running.
App extensions, XPC services, login items, URL schemes, App Groups, and Keychain access groups can embed or share identity. Apple’s guidance on changing a bundle identifier is a useful starting point. If the target graph cannot safely support several identities, keep builds and test data isolated but serialize interactive app launches.
A Mac app may also depend on a local API, database, helper daemon, Docker container, mock server, or webhook tunnel. Give each task predictable ports, service names, database state, logs, and a scoped stop command. Cleanup must address the same resources setup created.
Cleanup belongs to the lifecycle
Every task should have an inventory of resources to remove:
- DerivedData, archives, build products, and task-owned caches.
- App containers, app-support data, preferences, and test fixtures.
- Local databases.
- Docker containers and volumes.
- Ports, helper processes, and local services.
- Logs, screenshots, recordings, and generated evidence.
- Copied environment files and generated configuration.
Xcode build output and caches make cleanup a practical concern. Several worktrees can consume a surprising amount of disk space.
Cleanup must be deterministic and scoped to the task. Every resource needs the same stable task identity so the cleanup command removes only what setup created for that worktree. Do not run a broad cleanup command unless it can prove its target.
An agent can audit setup and generate the corresponding cleanup path. In Neroli,
a repository-owned Workspace Command can use the session lifecycle removed
event. A before trigger lets cleanup run while the worktree and its
configuration are still available.
2. Make the repository easy for agents to operate
Agents need more than access to source code. They need to build, run, test, and clean up the project without reconstructing undocumented setup on every task.
Start with an audit. Ask the agent to map the Xcode projects, schemes, targets, extensions, capabilities, build wrappers, app-data paths, ports, databases, containers, simulators, credentials, and release boundaries. It should propose an isolation matrix before changing the setup. Mature applications usually need several real sessions before every hidden dependency is discovered.
Keep deterministic work out of the prompt. In Neroli, repository-owned
Workspace Commands run scripts
for setup, build, services, checks, and cleanup without spending an agent turn.
Use an agent automation only when the work needs model reasoning. A Workspace
Command can use an after created session lifecycle trigger so every new task
starts from the same prepared environment.
The setup command conceptually says, “Make this worktree real.” Given the worktree path, it should derive a stable task identity, assign ports and service names, prepare DerivedData and app-data directories, initialize local databases, copy only allowlisted development configuration, and write any generated local settings. It should be idempotent: running it again converges on the same safe state.
The command works only if the application accepts the configuration it writes. Replace hardcoded development ports, local URLs, database paths, service names, bundle suffixes, and data directories with explicit Xcode build settings, environment variables, command-line arguments, or generated untracked configuration. Keep production values, credentials, and signing authority outside this path. A setup script cannot isolate a resource that the application always hardcodes.
From task to pull request
- Define one bounded change with observable acceptance criteria.
- Create the branch, worktree, and session; let the lifecycle command prepare its configuration and resources.
- Let the agent implement inside that boundary using the repository’s build, run, and test commands.
- Verify the exact build, beginning with focused tests and then the broader repository check.
- Open a pull request with the diff, checks, risks, and visual evidence; after merge, run only that task’s cleanup path.
Each task should end in its own pull request. Required checks, review comments, and evidence stay attached to the change. When a reviewer requests a revision, the agent continues in the existing workspace rather than recreating its context elsewhere. GitHub can enforce required status checks on the exact pull-request head.
3. Make human verification cheap
Review should not require rebuilding the agent’s environment by hand. The reviewer needs a short path from “the agent says it is done” to “I personally saw it work.”
Give the agent a real runtime
Agents should have access to a simulator or equivalent real app environment so they can:
- Build the app.
- Launch the correct task-owned copy.
- Drive the relevant user flow.
- Capture screenshots or a short recording.
- Iterate on failures.
- Verify the result before handing it back.
For a UI change, coding-agent verification should combine tests nearest the changed code with the affected app build and a real interaction. Apple’s Xcode testing guidance covers unit, integration, and UI testing. Evidence should identify the commit, app build, interaction, and environment it demonstrates.
Development helpers can remove repetitive setup. A guarded development-only flag or fixture might bypass an external login flow so an agent can reach the feature under test quickly. Such paths should be excluded from production builds where possible, require an explicit development opt-in, and never grant production credentials or authority.
Because code generation is inexpensive, a project can also create focused programmatic harnesses that launch a real environment and drive one important flow. These complement broader end-to-end suites when they make a regression faster to reproduce and inspect.
Proof and walkthroughs
A successful build does not prove every change. The agent should verify behavior in the real simulator or app instance and capture evidence. Neroli Walkthroughs can record the final flow and its before-and-after behavior, so a reviewer knows what changed without first rediscovering it.
Walkthroughs and screenshots do not replace human review. They narrow the reviewer’s attention and show what to inspect.
Give the reviewer one run button
The ideal review path is deliberately boring:
- The agent provides a walkthrough or other proof showing what changed.
- The reviewer runs the repository’s saved launch command.
- The exact task-owned build launches with its dependencies prepared.
- The app lands at or near the changed flow.
- The reviewer tests it directly and merges if satisfied.
For a small, well-scoped UI change with strong proof, that review can be quick. Changes involving backends, migrations, entitlements, signing, or production systems deserve more scrutiny. The factory should make verification cheaper, and automation should not be treated as a reason to skip review.
Release signing, notarization, and publication should remain human-controlled and run from a clean checkout of the protected branch. Apple documents that distribution path in Creating distribution-signed code for macOS.
Start with one repeatable path
Begin with one repository and one representative feature. Let an agent audit the setup, then create supported commands for setup, build, run, focused tests, the branch check, and cleanup. Make one simulator flow reproducible and give a reviewer one button that launches the exact task-owned build.
Run that path through two independent sessions. Fix the collisions and hidden assumptions they reveal. Add more concurrency only when setup remains deterministic, cleanup stays scoped, and a reviewer can still understand which build and evidence belong to which change.
You can build this setup with Git, shell scripts, Xcode, containers, and
continuous integration. Neroli puts the pieces in one place: worktree-backed
sessions, created and removed lifecycle triggers, Workspace Commands, agent
automations, simulator interaction, saved run buttons, and walkthrough evidence.
The interactive playbook below asks an agent to audit a repository, generate its lifecycle commands, and review the first real trial.
Common questions
Should every AI coding agent get its own Git worktree?
Every independent task should. A task may use more than one agent, but concurrent writers need explicit ownership. Read-only review agents can share the task context without getting another writable checkout.
Why not use ordinary Git branches?
Branches separate history, but only one branch can occupy a normal checkout at a time. Worktrees let several branches have separate directories simultaneously, which is what parallel local agents need.
Should each worktree have a complete copy of dependencies?
It should have an independently usable environment. Immutable download caches can be shared for speed; mutable build outputs, generated sources, databases, and runtime state should be isolated or coordinated.
Can several Xcode builds run at once?
Yes, especially with separate DerivedData, but unlimited concurrency is rarely efficient on one Mac. Queue heavy builds according to machine capacity while agents continue planning, editing, and reviewing in parallel.
Can several copies of the Mac app run at once?
Yes if their debug identities, data, and services are truly separate and the test harness can target the correct running copy. Apps with complex extensions or entitlements may be safer with isolated builds and tests but serialized interactive launches.
Should agents merge their own pull requests?
Merge authority is a team policy. Even when low-risk changes may auto-merge after required checks, the factory should keep the proof and exact build easy for a person to inspect.
What is the difference between a screenshot and a walkthrough?
A screenshot proves one rendered state. A walkthrough shows the sequence that produced it, which is better for interactions, transitions, focus behavior, and animation. Both should identify the exact build being demonstrated.
Is a passing PR enough?
A passing PR is the main code gate. UI changes still benefit from evidence and a quick way to launch the exact build. Verify the merged commit when combined changes or generated artifacts could differ, and keep production release authority separate.
Setup prompt
-
Paste it into your agent
Start a coding-agent session at the repository's primary checkout, then paste the prompt below.
Inspect this Xcode repository and help make it work well with parallel Neroli sessions.
We want each session and worktree to be easy to set up, build, run, test, review,
and clean up. The repository should own the deterministic commands. Neroli should
expose them as Workspace Commands and handle simulator interaction, screenshots,
walkthroughs, and reviewer evidence.
Start by reading the repository instructions and inspecting the existing Xcode
projects, schemes, targets, scripts, and development setup. Check the available
Neroli CLI instead of guessing how it works.
Consider the whole environment needed to run the current worktree, including
backend services, ports, databases, containers, environment configuration, app
data, and build output. Focus on the resources this repository actually uses.
Propose the smallest approach that fits this repository. Include Setup, Build,
Run, Focused Tests, Check, and Cleanup. Run should quickly prepare, incrementally
build, and launch the app from the current worktree, whether it is a macOS or iOS
app. If the repository supports both, propose a clear platform choice.
Explain what can be isolated per worktree, what should remain shared or
serialized, and any signing or data risks you find. Ask questions where the
right choice depends on the project or the team.
Stop after the proposal and wait for approval before changing files.
After approval, implement the repository-native commands, expose the useful ones
in Neroli, and try the workflow in temporary worktrees. Use Neroli's simulator
and built-in proof tools instead of creating a separate evidence system. Keep
production signing, credentials, and user data out of the experiment.
Finish by showing how an agent or reviewer runs the app from the current
worktree and how the task cleans up after itself.