# How SwarmForge Ensures Worktree Isolation for Agents: Git Worktree Architecture Explained

> SwarmForge isolates agent worktrees in .worktrees/ to prevent file and history conflicts. Learn how Git worktrees ensure concurrent role isolation.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: internals
- Published: 2026-08-09

---

**SwarmForge isolates every agent in its own Git worktree under the `.worktrees/` directory to prevent concurrent roles from interfering with each other's files or repository history.**

SwarmForge, the open-source multi-agent orchestration framework maintained by Uncle Bob (`unclebob/swarm-forge`), implements comprehensive worktree isolation to enable concurrent AI agent operations without filesystem conflicts. By leveraging Git's native worktree capabilities combined with programmatic directory management, SwarmForge ensures that each role operates within a completely separate working directory while sharing only the underlying object database. This article examines the specific Clojure functions and architectural patterns in `swarmforge/scripts/swarmforge.bb` that enforce this isolation.

## Dedicated Worktree Directory Allocation

SwarmForge assigns every non-master role a private filesystem location under the repository root's hidden `.worktrees/` folder. The `worktree-path-for-name` function constructs these absolute paths deterministically:

```clojure
(defn worktree-path-for-name [worktrees-dir worktree]
  (fs/path worktrees-dir worktree))

```

This helper ensures consistent path resolution throughout the application lifecycle, mapping logical role names (like `coder` or `reviewer`) to physical directories (such as `.worktrees/coder`).

## Automated Git Worktree Creation

During initialization, the `prepare-worktrees!` function iterates over every role configuration to establish Git worktrees for roles not assigned to `master` or `none`. As implemented in `swarmforge/scripts/swarmforge.bb` (lines 251-259), the function checks for existing `.git` directories before invoking `git worktree add`:

```clojure
(defn prepare-worktrees! [ctx]
  (doseq [row (:roles ctx)
          :let [worktree-name (:worktree-name row)
                worktree-path (:worktree-path row)
                branch-name (str "swarmforge-" worktree-name)]
          :when (not (#{"none" "master"} worktree-name))]
    (when-not (or (fs/exists? (fs/path worktree-path ".git"))
                  (fs/directory? (fs/path worktree-path ".git")))
      (sh "git" "-C" (str (:working-dir ctx))
          "worktree" "add" "--force" "-B" branch-name (str worktree-path) "HEAD"))))

```

This command creates a fresh worktree pointing to the current `HEAD` on a dedicated branch (prefixed with `swarmforge-`), ensuring each agent starts with an isolated copy of the repository that shares only the object database.

## Git Ignore and Exclude Protections

To prevent agents from accidentally committing generated state or interfering with other worktrees, SwarmForge maintains exclusion rules at two levels. First, `ensure-initial-gitignore!` updates the repository's top-level `.gitignore`:

```clojure
(defn ensure-initial-gitignore! [ctx]
  (let [gitignore (fs/path (:working-dir ctx) ".gitignore")]
    (if-not (fs/exists? gitignore)
      (spit (str gitignore) ".swarmforge/\n.worktrees/\n")
      (do (ensure-in-file! gitignore ".swarmforge/")
          (ensure-in-file! gitignore ".worktrees/")))))

```

Second, `ensure-runtime-git-excludes!` updates the per-worktree `info/exclude` file:

```clojure
(defn ensure-runtime-git-excludes! [ctx]
  (let [exclude-file (fs/path (sh-out "git" "-C" (str (:working-dir ctx))
                                     "rev-parse" "--git-path" "info/exclude"))]
    (fs/create-dirs (fs/parent exclude-file))
    (ensure-in-file! exclude-file ".swarmforge/")
    (ensure-in-file! exclude-file ".worktrees/")))

```

These dual protections ensure that `.swarmforge/` runtime directories and sibling worktrees remain invisible to Git operations regardless of which worktree is active.

## Script and State Synchronization

After creating a worktree, SwarmForge copies shared resources into each isolated environment via `sync-worktree-scripts!`. This function duplicates the `swarmforge/scripts/` directory and runtime configuration files into the worktree's local `.swarmforge/` folder:

```clojure
(defn sync-worktree-scripts! [ctx]
  (doseq [row (:roles ctx)
          :let [worktree-path (:worktree-path row)]
          :when (not= (str worktree-path) (str (:working-dir ctx)))]
    (let [role-scripts-dir (fs/path worktree-path "swarmforge" "scripts")
          role-state-dir   (fs/path worktree-path ".swarmforge")]
      (fs/create-dirs role-scripts-dir)
      (doseq [entry (fs/list-dir (:script-dir ctx))]
        (let [target (fs/path role-scripts-dir (fs/file-name entry))]
          (if (fs/directory? entry)
            (fs/copy-tree entry target {:replace-existing true})
            (fs/copy entry target {:replace-existing true}))))
      (fs/create-dirs (fs/path role-state-dir "notify"))
      (fs/copy (:sessions-file ctx) (fs/path role-state-dir "sessions.tsv") {:replace-existing true})
      (fs/copy (:roles-file ctx)    (fs/path role-state-dir "roles.tsv")    {:replace-existing true})
      (fs/copy (:tmux-socket-file ctx) (fs/path role-state-dir "tmux-socket") {:replace-existing true})
      (fs/copy (:tmux-env-file ctx)    (fs/path role-state-dir "tmux-env")    {:replace-existing true}))))

```

This synchronization ensures every agent executes local copies of helper scripts and maintains independent session state, eliminating cross-worktree dependencies.

## Configuration-Driven Isolation Mapping

Worktree assignment originates in the project's configuration file (typically [`swarmforge.conf`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge.conf)), where each role specifies a `worktree` field. During configuration parsing, the system resolves either the main repository path (for `master` or `none`) or a dedicated worktree path:

```clojure
(let [worktree-path (if (#{"none" "master"} worktree)
                      working-dir
                      (worktree-path-for-name worktrees-dir worktree))
      row {:role role
           :worktree-name worktree
           :worktree-path worktree-path
           ...}]

```

This mapping binds each role to its specific filesystem location before any agent launches, ensuring deterministic path resolution throughout the orchestration lifecycle.

## Runtime Path Enforcement

Finally, SwarmForge enforces isolation at execution time through the `launch-command` function. By explicitly changing into the role's assigned worktree directory before executing the agent command, the system confines all file operations to the isolated environment:

```clojure
(defn launch-command [ctx index row]
  (let [role-worktree (:worktree-path row)
        base (str "export SWARMFORGE_ROLE=" (sq role)
                  " && export PATH=" (sq (str role-script-dir)) ":$PATH"
                  " && cd " (sq (str role-worktree)) " && ")]
    ...))

```

This `cd` operation ensures that builds, tests, and file modifications occur exclusively within the agent's designated worktree, preventing accidental contamination of the master branch or other roles' workspaces.

## Summary

- **Filesystem isolation**: Each role receives a dedicated directory under `.worktrees/` via `worktree-path-for-name`, physically separating agent workspaces.
- **Git-native isolation**: The `prepare-worktrees!` function utilizes `git worktree add` to create linked working trees sharing only the object database.
- **VCS protection**: Dual `.gitignore` and `info/exclude` updates prevent accidental commits of runtime state via `ensure-initial-gitignore!` and `ensure-runtime-git-excludes!`.
- **Self-contained execution**: `sync-worktree-scripts!` copies all dependencies into each worktree, ensuring agents never reference external paths.
- **Immutable mapping**: Configuration parsing binds roles to paths before launch, while `launch-command` enforces the directory constraint at runtime.

## Frequently Asked Questions

### How does SwarmForge prevent agents from accessing files outside their worktree?

SwarmForge enforces filesystem boundaries through multiple mechanisms. The `launch-command` function explicitly executes a `cd` into the role's `worktree-path` before starting the agent, ensuring all relative file operations remain within that directory. Additionally, the `sync-worktree-scripts!` function provides local copies of all necessary scripts and configuration files inside the worktree's `.swarmforge/` directory, eliminating the need for agents to traverse outside their isolated environment.

### What happens if a worktree already exists when SwarmForge starts?

The `prepare-worktrees!` function checks for existing Git metadata before attempting creation. Specifically, it verifies whether `(fs/path worktree-path ".git")` exists as either a file or directory. If the path already contains Git tracking information, SwarmForge skips the `git worktree add` command entirely, preserving existing state while still proceeding with script synchronization and runtime configuration.

### Can multiple agents safely operate on the same branch using SwarmForge worktree isolation?

No, SwarmForge explicitly prevents this scenario by creating dedicated branches for each worktree. The `prepare-worktrees!` function constructs branch names using the pattern `(str "swarmforge-" worktree-name)`, ensuring each isolated worktree tracks a unique branch (e.g., `swarmforge-coder`). This prevents conflicting file modifications between agents while allowing the master repository to remain untouched until changes are explicitly merged.

### How does SwarmForge handle cleanup when an agent terminates?

While the source code focuses on initialization rather than termination, the architecture implies cleanup occurs at the Git worktree level. Since each agent operates within a standard Git worktree created via `git worktree add`, administrators can remove isolated environments using standard Git commands (`git worktree remove`) or simply delete the `.worktrees/<role-name>` directory, as the main repository maintains no hard dependencies on these transient working directories.