Skip to content

Worktree Conventions

This guide explains how to structure and manage Git worktrees for project work in the Arda workspace. Follow these conventions whether you are working alone or coordinating a multi-agent team.

Arda projects routinely span multiple repositories. When a feature touches both operations and common-module, for example, you need to make changes in two separate repositories simultaneously. Working directly in the main clone of each repository creates two problems:

  • Switching between branches in the main clone interrupts other in-flight work.
  • Two parallel agents writing to the same clone without coordination produce merge conflicts.

Git worktrees let you check out a separate branch into a separate directory while sharing the repository’s object store. You can have as many worktrees as you need — one per repository per task — each on its own branch. The main clone is always clean and ready for review or unrelated work.

Worktrees also make rollback clean. Abandoning a failed task is a single git worktree remove and git branch -D, with no residue in the main clone.

New projects use a grouped parent directory at the workspace root:

arda/
├── projects/
│ └── <project-name>-worktrees/ # parent directory for this project's worktrees
│ ├── <repo>-base/ # consolidation or multi-agent base worktree
│ ├── <repo>-<task>/ # task-specific worktree for a given repo
│ ├── documentation/ # worktree for documentation changes
│ └── workspace/ # worktree for skill or config changes

For example, a project called inventory with tasks registry and builder would produce:

arda/
├── projects/
│ └── inventory-worktrees/
│ ├── operations-registry/
│ ├── operations-builder/
│ ├── common-module-base/
│ └── documentation/

Earlier projects placed worktrees directly at the workspace root:

arda/
├── <project>-worktrees/
│ └── <task>/

You may encounter this layout in older projects. Do not create new projects using this pattern.

All branches include a GitHub username prefix so that branches are clearly attributed and can coexist without collision in the shared remote.

ContextPatternExample
Human-driven work<github-username>/<description>jmpicnic/add-bulk-import
Agent-driven work<github-username>-ag/<description>-<ticket-repo>-<ticket-number>jmpicnic-ag/fix-delete-management-782
Integration branch<github-username>/<project>/integrationjmpicnic/inventory/integration
Task branch (multi-agent)<github-username>/<project>/<task>jmpicnic/inventory/registry

When you are working alone on a feature that touches one repository, create one worktree:

Terminal window
# Create the parent directory
mkdir -p /Users/jmp/code/arda/projects/my-feature-worktrees
# Create the worktree on a new branch
git -C /Users/jmp/code/arda/operations worktree add \
/Users/jmp/code/arda/projects/my-feature-worktrees/operations \
-b jmpicnic/my-feature/operations

If the feature also touches documentation:

Terminal window
git -C /Users/jmp/code/arda/documentation worktree add \
/Users/jmp/code/arda/projects/my-feature-worktrees/documentation \
-b jmpicnic/my-feature/documentation

When multiple agents work in parallel on the same repository, add a base worktree for consolidation and a separate task worktree for each agent:

Terminal window
# Integration branch as the base
git -C /Users/jmp/code/arda/operations worktree add \
/Users/jmp/code/arda/projects/inventory-worktrees/operations-base \
-b jmpicnic/inventory/integration
# Task worktrees branched off integration
git -C /Users/jmp/code/arda/operations worktree add \
/Users/jmp/code/arda/projects/inventory-worktrees/operations-registry \
-b jmpicnic/inventory/registry
git -C /Users/jmp/code/arda/operations worktree add \
/Users/jmp/code/arda/projects/inventory-worktrees/operations-builder \
-b jmpicnic/inventory/builder

Each agent works exclusively in its assigned worktree. After all tasks complete, merge each task branch into the integration branch:

Terminal window
git -C /Users/jmp/code/arda/projects/inventory-worktrees/operations-base \
merge --no-ff jmpicnic/inventory/registry
git -C /Users/jmp/code/arda/projects/inventory-worktrees/operations-base \
merge --no-ff jmpicnic/inventory/builder

Then open a single PR from jmpicnic/inventory/integration to main.

The integration branch strategy isolates project work from main until all tasks are verified and ready to ship together.

main
└── jmpicnic/inventory/integration (integration branch, opened as one PR to main)
├── jmpicnic/inventory/registry (task branch, merged into integration)
└── jmpicnic/inventory/builder (task branch, merged into integration)

Steps:

  1. Create the integration branch off main before creating any task branches.
  2. Create task branches off the integration branch.
  3. Agents work independently on their task branches.
  4. When each task is complete, merge its branch into the integration branch. Resolve any conflicts here, not on main.
  5. When all tasks are merged and the integration branch passes CI, open one PR to main.
  6. After the PR is merged, clean up worktrees and task branches.

These rules apply to all project work, whether human-driven or agent-driven:

  • Always use absolute paths in agent tool calls. Agents inherit the spawning agent’s working directory, which may not match the worktree you intend. Relative paths silently operate on the wrong directory.
  • Never work directly in main clones for project work. Reserve main clones for reviewing, pulling updates, and unrelated ad-hoc changes.
  • Verify no uncommitted changes before removing a worktree. Run git -C <worktree-path> status first. Removing a worktree with uncommitted changes discards that work permanently.
  • Use workspace/instructions/claude/scripts/reset-worktrees.sh for phase-gate resets. This script verifies each worktree, resets branches to a known state, and re-applies the integration base. Do not reset worktrees manually when multiple task branches are involved.
  • Do not push task branches directly to main. All merges to main go through a PR. Task branches merge to the integration branch; the integration branch goes to main via PR.
  • Spell out worktree parent paths explicitly in permission allow-lists. The glob pattern *-worktrees/** does not reliably match hyphenated directory names in Claude Code’s permission system. List each worktree parent explicitly (e.g., inventory-worktrees/**).

Gradle Daemons Are Shared Across Worktrees

Section titled “Gradle Daemons Are Shared Across Worktrees”

Gradle keys its daemons on JVM arguments and GRADLE_USER_HOME, never on the project directory. Two worktrees of the same repository therefore share a daemon — whichever compatible one already exists — and neither knows the other is using it.

The consequence arrives as someone else’s failure. Any client that judges a compatible daemon stale or incompatible issues a stop, and that stop takes down whatever build is running on it. One night of four concurrent sessions produced 46 such stops; one daemon log carried two worktrees’ paths, and one stop landed mid-build while a container was starting.

Give each worktree its own Gradle home:

Terminal window
./gradlew -g ./.gradle-user-home <task>
# or export GRADLE_USER_HOME=$PWD/.gradle-user-home

Two things make this less simple than it looks.

It is convention, not enforcement. Nothing makes it happen; every caller must remember the flag. One repository adopted it across three worktrees and silently stopped using it, which is how the sharing was rediscovered.

A fresh isolated home has no gradle.properties, so package-registry credentials go missing — and the resulting failure reports the dependency as not found rather than as unauthorized. Seed that file when you create the home, or the fix produces a harder problem than the one it solved. That disguise is not specific to worktrees: see Package Registry Auth Failures.

Isolation costs a per-worktree artifact cache in disk and first-run time; a fresh home spends its first run resolving rather than compiling.

When a project is complete and all PRs are merged, remove the worktrees and their branches.

1. Verify each worktree has no uncommitted changes:

Terminal window
git -C /Users/jmp/code/arda/projects/inventory-worktrees/operations-registry status
git -C /Users/jmp/code/arda/projects/inventory-worktrees/operations-builder status

2. Remove each worktree:

Terminal window
git -C /Users/jmp/code/arda/operations worktree remove \
/Users/jmp/code/arda/projects/inventory-worktrees/operations-registry
git -C /Users/jmp/code/arda/operations worktree remove \
/Users/jmp/code/arda/projects/inventory-worktrees/operations-builder
git -C /Users/jmp/code/arda/operations worktree remove \
/Users/jmp/code/arda/projects/inventory-worktrees/operations-base

3. Prune stale worktree metadata:

Terminal window
git -C /Users/jmp/code/arda/operations worktree prune

4. Delete the task branches (after confirming they are merged):

Terminal window
git -C /Users/jmp/code/arda/operations branch -d jmpicnic/inventory/registry
git -C /Users/jmp/code/arda/operations branch -d jmpicnic/inventory/builder
git -C /Users/jmp/code/arda/operations branch -d jmpicnic/inventory/integration

5. Remove the empty parent directory:

Terminal window
rmdir /Users/jmp/code/arda/projects/inventory-worktrees

Copyright: (c) Arda Systems 2025-2026, All rights reserved