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:
- Branch generation: Creates a temporary branch name via
generateTempBranchName - Worktree creation: Establishes a worktree for that branch using
src/createWorktree.ts - Agent execution: Runs the agent inside the isolated worktree
- Host-side merge: Upon completion,
SandboxLifecycle.tsexecutesgit mergeto bring changes into the host’s original branch - 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 selectsmerge-to-headfor isolated sandboxes andheadfor bind-mount sandboxes unless overridden. - Validation:
headis illegal with isolated providers, andcopyToWorktreecannot be used withhead.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →