# Sandcastle Branch Strategies: head, merge-to-head, and branch Explained

> Understand Sandcastle branch strategies head merge-to-head and branch. Learn how AI agents write and reconcile code in your repository for efficient development.

- Repository: [Matt Pocock/sandcastle](https://github.com/mattpocock/sandcastle)
- Tags: deep-dive
- Published: 2026-05-24

---

**The three Sandcastle branch strategies determine where an AI agent writes code and how those changes reconcile with the host repository: `head` writes directly to the host working directory (bind-mount only), `merge-to-head` creates a temporary branch that merges back after completion, and `branch` checks out a specific named branch for the agent to modify.**

Sandcastle, an open-source agentic coding framework by Matt Pocock, uses **branch strategies** to abstract Git workflow management. These strategies, defined in [`src/SandboxProvider.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/SandboxProvider.ts), control whether the sandbox operates directly on your filesystem or within an isolated Git context, ensuring compatibility across Docker, Podman, Vercel, and no-sandbox providers.

## The Three Branch Strategy Types

The `BranchStrategy` union type in [`src/SandboxProvider.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/SandboxProvider.ts) supports three distinct modes【SandboxProvider.ts†L44-L89】. Each strategy differs in isolation level, provider compatibility, and post-run reconciliation behavior.

### Head Strategy: Direct Host Write

The `head` strategy instructs the agent to write **directly to the host working directory** without creating a temporary branch. No merge step occurs after the run because the host branch already contains the agent’s changes.

- **Location**: Host filesystem (`currentHostBranch`)
- **Providers**: Bind-mount only (Docker, Podman, Vercel) and `no-sandbox`
- **Performance**: Fastest option with zero Git overhead
- **Risk**: Directly mutates the user’s visible working tree

This strategy is illegal for isolated providers because they cannot write to the host file-system【run.ts†L33-L38】.

### Merge-to-Head Strategy: Temporary Branch Workflow

The `merge-to-head` strategy creates a **temporary branch** inside a disposable worktree. The sandbox sees this temporary branch as its `HEAD`, keeping the host working directory untouched during execution.

After the agent finishes, Sandcastle executes a host-side merge step in [`src/SandboxLifecycle.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/SandboxLifecycle.ts) (around lines 360-380), running `git merge "<temp-branch>"` to reconcile changes back into the original target branch, then deletes the temporary branch【SandboxProvider.ts†L44-L89】.

- **Location**: Temporary worktree branch
- **Providers**: All providers (isolated and bind-mount)
- **Default**: Automatically selected for isolated providers (Docker, Podman, Daytona)
- **Safety**: Protects against accidental overwrites; easy rollback by deleting the temp branch

### Branch Strategy: Named Persistent Branch

The third strategy, `branch`, directs the agent to work on a **specifically named branch** (e.g., `feature/auto-fix`). Unlike `merge-to-head`, this branch persists after the run and is not automatically deleted or merged.

- **Location**: Named branch in the repository
- **Providers**: All providers (unlike `head`, which is bind-mount only)
- **Use case**: When you need the agent to contribute to a long-running feature branch rather than a temporary scratch space

## How Sandcastle Selects the Default Strategy

If you do not explicitly provide a `branchStrategy` option, `run()` derives the strategy automatically based on the provider’s sandbox tag. The logic resides in [`src/run.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/run.ts) lines 25-33【run.ts†L25-L33】:

```typescript
const branchStrategy: BranchStrategy =
  options.branchStrategy ??
  (options.sandbox.tag === "isolated"
    ? { type: "merge-to-head" }
    : { type: "head" });

```

- **Isolated providers** (`sandbox.tag === "isolated"`): Default to `{ type: "merge-to-head" }`
- **Bind-mount providers** (`sandbox.tag === "bind-mount"`): Default to `{ type: "head" }`

## Validation Rules and Constraints

Sandcastle validates strategy-provider compatibility early in the execution flow to prevent filesystem errors.

### Head Strategy Restrictions

Using `head` with an isolated provider triggers an immediate error (run.ts lines 33-38)【run.ts„L33-L38】. This prevents the agent from attempting to write to a host directory it cannot access.

### Copy-to-Worktree Conflict

The `copyToWorktree` option is disallowed with the `head` strategy because bind-mount providers already share the host directory, making the copy operation redundant and potentially conflicting (run.ts lines 40-49)【run.ts†L40-L49】.

## The Merge-to-Head Workflow Under the Hood

When `merge-to-head` is active, Sandcastle orchestrates a multi-step Git workflow:

1. **Branch generation**: Creates a temporary branch name via `generateTempBranchName`
2. **Worktree creation**: Establishes a worktree for that branch using [`src/createWorktree.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/createWorktree.ts)
3. **Agent execution**: Runs the agent inside the isolated worktree
4. **Host-side merge**: Upon completion, [`SandboxLifecycle.ts`](https://github.com/mattpocock/sandcastle/blob/main/SandboxLifecycle.ts) executes `git merge` to bring changes into the host’s original branch
5. **Cleanup**: Deletes the temporary branch and worktree

This process ensures the host repository remains in a clean state until the agent successfully completes its task.

## Practical Code Examples

### Default Merge-to-Head with an Isolated Docker Sandbox

Isolated Docker sandboxes automatically use `merge-to-head`, protecting your working directory while the agent runs inside a container.

```typescript
import { docker } from "sandcastle/sandboxes/docker";
import { claudeCode } from "sandcastle/agents/claude";
import { run } from "sandcastle";

await run({
  agent: claudeCode("claude-opus-4-7"),
  sandbox: docker({ imageName: "sandcastle:myrepo" }),
  prompt: "Refactor the authentication logic in src/auth.ts",
});

```

### Explicit Head Strategy with Bind-Mount Docker

For faster execution when using bind-mount providers, explicitly request the `head` strategy to eliminate branch creation overhead.

```typescript
await run({
  agent: claudeCode("claude-opus-4-7"),
  sandbox: docker({ imageName: "sandcastle:myrepo" }), // bind-mount configuration
  branchStrategy: { type: "head" },
  prompt: "Update the README with installation instructions",
});

```

### Head Strategy with No-Sandbox Provider

The `no-sandbox` provider runs agents directly on the host machine, making it compatible with the `head` strategy for maximum performance.

```typescript
import { noSandbox } from "sandcastle/sandboxes/no-sandbox";

await run({
  agent: claudeCode("claude-opus-4-7"),
  sandbox: noSandbox(),
  branchStrategy: { type: "head" },
  prompt: "Format all TypeScript files in the project",
});

```

### Named Branch Strategy for Feature Work

Use the `branch` strategy when you need the agent to commit to a specific, persistent feature branch rather than a temporary one.

```typescript
await run({
  agent: claudeCode("claude-opus-4-7"),
  sandbox: docker({ imageName: "sandcastle:myrepo" }),
  branchStrategy: { type: "branch", branch: "feature/api-refactor" },
  prompt: "Implement the new REST endpoints according to the OpenAPI spec",
});

```

## Summary

- **Head** ([`src/SandboxProvider.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/SandboxProvider.ts)): Writes directly to the host working directory; fastest but only compatible with bind-mount and no-sandbox providers.
- **Merge-to-Head**: Creates a temporary branch and worktree, merging back after completion; the default for isolated providers and the safest option for Docker/Podman workflows.
- **Branch**: Checks out a specific named branch for the agent to modify; available for all providers when you need persistent feature branches.
- **Defaults**: `run()` automatically selects `merge-to-head` for isolated sandboxes and `head` for bind-mount sandboxes unless overridden.
- **Validation**: `head` is illegal with isolated providers, and `copyToWorktree` cannot be used with `head`.

## Frequently Asked Questions

### Can I use the head strategy with Docker?

Only if Docker is configured as a **bind-mount** provider. Standard **isolated** Docker sandboxes cannot use `head` because they lack filesystem access to the host working directory. Attempting to force `head` with an isolated provider throws a validation error in [`src/run.ts`](https://github.com/mattpocock/sandcastle/blob/main/src/run.ts)【run.ts†L33-L38】.

### What happens if a merge-to-head run fails or is interrupted?

The temporary branch remains in your repository until you manually delete it. This behavior acts as a safety mechanism—you can inspect the partial work in the temporary branch or discard it entirely without affecting your original target branch. The merge back to `HEAD` only occurs if the agent run completes successfully.

### When should I choose the branch strategy over merge-to-head?

Choose the `branch` strategy when you need the agent to work on a **pre-existing or long-running feature branch** that should persist after the session ends. Unlike `merge-to-head`, which deletes the temporary branch after reconciling changes, the `branch` strategy leaves the named branch intact, making it ideal for multi-step feature development or CI/CD pipelines that expect specific branch names.

### Why does Sandcastle disallow copyToWorktree with the head strategy?

The `copyToWorktree` option is redundant and potentially conflicting when combined with `head` because bind-mount providers already share the host directory directly with the sandbox. Copying files would duplicate state and could overwrite uncommitted changes, so Sandcastle explicitly blocks this combination in the validation logic【run.ts†L40-L49】.