# How Per-Agent Git Worktrees Are Created and Managed for Isolation in Munder Difflin

> Discover how Munder Difflin uses per-agent Git worktrees for isolation. Learn about unique branches, directory mapping, and persistent uncommitted work across agent restarts.

- Repository: [Chaitanya Giri/munder-difflin](https://github.com/chaitanyagiri/munder-difflin)
- Tags: internals
- Published: 2026-08-20

---

**Munder Difflin creates a dedicated Git worktree for every isolated agent, mapping each worker to a unique branch and directory while preserving uncommitted work across restarts through a reference-counted persistence layer.**

The [chaitanyagiri/munder-difflin](https://github.com/chaitanyagiri/munder-difflin) repository implements a robust per-agent git worktrees strategy that allows concurrent AI agents to operate on the same base repository without file-system collisions. By leveraging Git worktrees and maintaining precise metadata maps, the system ensures that each worker’s changes remain isolated while providing automatic cleanup and recovery mechanisms.

## Architecture of Worktree Isolation

The isolation layer relies on three core data structures maintained in the main process: `worktreePaths` (mapping agent IDs to absolute worktree directories), `worktreeOrigins` (mapping agent IDs to the original repository root), and `preservedWorktrees` (tracking worktrees with pending changes). These maps coordinate the lifecycle from creation through teardown.

### Worktree Creation via spawnPty

When an agent spawns with the `isolate: true` flag, the main process invokes the `spawnPty` function, which triggers the worktree creation protocol. The system first validates that the current working directory is a Git repository using `gitIsRepo`, then allocates a new worktree via the `git:addWorktree` IPC channel.

According to the source in [[`src/main/index.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/index.ts)](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/index.ts#L315-L322), the creation logic records the mapping:

```typescript
// src/main/index.ts – worktree creation during spawn
if (isolate && await gitIsRepo(cwd)) {
  const { worktreePath } = await ipcRenderer.invoke('git:addWorktree', cwd);
  worktreePaths.set(id, worktreePath);
  worktreeOrigins.set(id, cwd);
  opts.cwd = worktreePath;  // PTY operates inside the isolated worktree
}

```

The worktree is created on a temporary branch (typically named `agent-<id>`) under a dedicated directory structure such as `HIVE_ROOT/agents/<agent-id>/worktree`, ensuring complete filesystem isolation from the base repository.

### Branch Naming and Directory Layout

Each isolated worktree derives from the current HEAD of the base repository but operates on a distinct branch. This branching strategy prevents merge conflicts between concurrent agents while allowing the system to track which changes belong to which worker. The absolute path returned by the Git command is stored immediately in the `worktreePaths` Map for later reference during teardown operations.

## Persistence and Restoration

To survive application restarts, the system persists worktree metadata to disk. Each agent’s configuration record includes a `worktreePath` field that survives process termination, enabling seamless restoration of the exact working directory upon relaunch.

### Saving Worktree State

When an agent is first created, the `AddAgentModal` component captures the worktree path returned by the spawn process and commits it to the persistent roster. As implemented in [[`src/renderer/src/components/AddAgentModal.tsx`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/AddAgentModal.tsx)](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/components/AddAgentModal.tsx#L389-L391), the application stores:

```typescript
// src/renderer/src/components/AddAgentModal.tsx – persistence logic
const agentData = {
  ...baseConfig,
  worktreePath: spawnResponse.worktreePath,  // Persisted for restoration
};
await window.cth.rosterAddAgent(agentData);

```

This persistence layer ensures that worktrees outlive individual PTY sessions, preventing data loss during unexpected crashes or user-initiated restarts.

### Restoring Agents on Restart

The `useRestoreTeam` hook handles the reconciliation of persisted worktrees when the application initializes. It validates that the recorded path still contains a valid Git repository before reassigning it as the agent’s current working directory.

As shown in [[`src/renderer/src/hooks/useRestoreTeam.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/hooks/useRestoreTeam.ts)](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/hooks/useRestoreTeam.ts#L112-L130), the restoration logic includes fallback handling:

```typescript
// src/renderer/src/hooks/useRestoreTeam.ts – restoration with validation
if (a.worktreePath && await window.cth.gitIsRepo(a.worktreePath)) {
  cwd = a.worktreePath;               // Re-enter the exact worktree
  worktreeGone = false;
} else {
  console.warn(`[restore] worktree gone for ${a.id}; falling back to base`);
  a.worktreePath = undefined;        // Clear stale reference
}

```

If the worktree directory has been manually deleted or corrupted, the agent gracefully falls back to the base repository root rather than failing to initialize.

## Teardown and Garbage Collection

The system implements conditional teardown logic that distinguishes between ephemeral worktrees (safe to delete) and those containing valuable unintegrated work. This prevents accidental data loss while maintaining disk hygiene.

### Conditional Cleanup in teardownPty

When a worker’s PTY session terminates, the `teardownPty` function retrieves the worktree path from the `worktreePaths` Map and evaluates whether the directory contains uncommitted changes or unpushed commits. As defined in [[`src/main/index.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/index.ts)](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/index.ts#L410-L416):

```typescript
// src/main/index.ts – conditional teardown logic
const wtPath = worktreePaths.get(id);
if (wtPath) {
  const work = await worktreeHasUnintegratedWork(wtPath, worker.baseBranch);
  if (work.unintegrated) {
    console.warn(`[worker] PRESERVING worktree with unintegrated work`);
    preservedWorktrees.set(wtPath, { agentId: id, baseBranch: worker.baseBranch });
  } else {
    await ipcRenderer.invoke('git:removeWorktree', wtPath, origCwd);
  }
}

```

The `worktreeHasUnintegratedWork` utility, referenced at [[`src/main/index.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/index.ts)](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/index.ts#L461-L463), performs a diff against the base branch to detect pending changes before any destructive operation occurs.

### Preserved Worktree Lifecycle

Worktrees containing unintegrated changes are transferred to the `preservedWorktrees` Map rather than being deleted immediately. A periodic garbage collection routine (`gcPreservedWorktrees`) inspects these entries to determine if the work has been merged or if the directory has disappeared from disk, as noted in [[`src/main/index.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/index.ts)](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/index.ts#L374-L382).

The UI surface in [`WorkersTab.tsx`](https://github.com/chaitanyagiri/munder-difflin/blob/main/WorkersTab.tsx) exposes these preserved worktrees to the user, displaying the base branch and directory name so developers can manually integrate or discard stale work:

```tsx
// src/renderer/src/components/WorkersTab.tsx – preserved worktree UI
<span style={sectionHead}>Preserved worktrees ({preserved.length})</span>
{preserved.map(w => (
  <div key={w.path}>
    <span>base: {w.baseBranch}</span>
    <span>{basename(w.path)}</span>
  </div>
))}

```

## Safety Mechanisms

Before any worktree removal, the system verifies the absence of unintegrated work through Git status checks and branch comparisons. This safety check, combined with the persistence of `worktreeOrigins` (tracking the base repository for each worktree), ensures that the `git:removeWorktree` IPC call always targets the correct linked directory without affecting the main repository or other active agents.

## Summary

- **Per-agent git worktrees** provide filesystem isolation by spawning each worker in a unique Git worktree linked to a temporary branch.
- **Metadata maps** (`worktreePaths`, `worktreeOrigins`) maintained in the main process track the relationship between agent IDs and their physical directories.
- **Persistence layer** stores worktree paths in the agent roster, enabling restoration after application restarts via `useRestoreTeam`.
- **Conditional teardown** preserves worktrees containing uncommitted changes, moving them to a `preservedWorktrees` collection rather than deleting them.
- **Garbage collection** periodically re-evaluates preserved worktrees to determine if they can be safely removed after integration.
- **Safety checks** via `worktreeHasUnintegratedWork` prevent data loss during automatic cleanup routines.

## Frequently Asked Questions

### How does Munder Difflin prevent data loss when closing an agent with uncommitted changes?

The `teardownPty` function inspects the worktree for unintegrated work using `worktreeHasUnintegratedWork` before deletion. If it detects pending changes, the worktree is added to the `preservedWorktrees` Map instead of being removed, allowing the user to recover the work later through the WorkersTab interface.

### Can multiple agents share the same worktree simultaneously?

No. Each agent receives a unique worktree path generated via `git worktree add` with a distinct branch name. The `worktreePaths` Map enforces a one-to-one relationship between agent IDs and directories, ensuring complete isolation of file operations between concurrent workers.

### What happens to a worktree if the application crashes?

Because the worktree path is persisted to the roster in `AddAgentModal` and stored in [`src/main/roster.ts`](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/roster.ts), the `useRestoreTeam` hook can locate and reattach to the existing directory on restart. If the directory still exists and remains a valid Git repository (`gitIsRepo` returns true), the agent resumes using it; otherwise, it falls back to the base repository root.

### How are worktrees physically organized on disk?

Worktrees reside under a dedicated agents directory (typically `HIVE_ROOT/agents/<agent-id>/worktree`) and are created as linked worktrees from the base repository. This structure keeps agent-specific files separate from the base repository while maintaining a clear relationship through the `worktreeOrigins` Map in the main process.