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

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, 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 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 (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 lines 25-33【run.ts†L25-L33】:

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
  3. Agent execution: Runs the agent inside the isolated worktree
  4. Host-side merge: Upon completion, 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.

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.

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.

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.

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): 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【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】.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →