Comprehensive module reference for GitWorktreeSandbox (v3.5.0, PR #5998): - Lifecycle state machine diagram with complete state transitions - Key classes: GitWorktreeSandbox, SandboxContext, CommitResult, SandboxStatus - Branch naming convention with sanitisation examples - Error handling table including ValueError for git_timeout <= 0 - Rollback behaviour (pre-commit vs post-commit) - Integration with PlanLifecycleService pseudocode - Apply Summary output format - Testing guidance referencing actual BDD feature files: features/git_worktree_sandbox.feature features/git_worktree_apply.feature features/git_worktree_coverage_boost.feature - Updated CHANGELOG.md with entry under [Unreleased] ### Added - Updated CONTRIBUTORS.md with documentation contribution record ISSUES CLOSED: #8051
10 KiB
Git Worktree Sandbox Module
Package: cleveragents.infrastructure.sandbox
Introduced: v3.5.0 (PR #5998)
The git worktree sandbox provides isolated, branch-based staging for plan modifications against git-checkout resources. Changes are committed to a dedicated branch inside a temporary worktree and merged back to the original branch only when the plan is applied — leaving the working tree untouched until the operator explicitly approves.
For the sandbox protocol interface, see
docs/api/core.md.
For the plan lifecycle that invokes the sandbox, see
docs/architecture.md.
A git worktree is a linked working tree that shares the same git object database as the main repository but has its own checked-out branch and working directory. This means:
- Isolation: LLM-generated changes are committed to a dedicated branch
(
cleveragents/plan-<plan_id>) without touching the main working tree. - Auditability: Every plan execution produces a real git commit with a diff you can inspect before merging.
- Conflict detection:
git mergesurfaces conflicts that flat file copy would silently overwrite. - Rollback: The worktree branch can be deleted without affecting the main branch.
When a plan executes against a git-checkout resource, the LLM writes
files to an isolated git worktree rather than directly to the project
directory. This guarantees that:
- The original working tree is never modified until
plan applyis run. - Rollback is a single
git reset --hard— no file-by-file undo. - The diff between the base commit and the sandbox branch is always
available via
plan diff. - Concurrent plans on the same repository do not interfere with each other.
Non-git projects fall back to the original flat shutil.copy2 strategy.
Lifecycle
GitWorktreeSandbox(resource_id, original_path)
│
▼
sandbox.create(plan_id) → CREATED
│ Creates branch cleveragents/plan-<plan_id>
│ Creates temporary worktree at /tmp/ca-sandbox-<plan_id>-*/
│
▼
sandbox.get_path("src/x.py") → ACTIVE
│ Translates resource-relative path to worktree-absolute path
│ Actor writes files here
│
▼
sandbox.commit("message") → COMMITTED
│ git add -A && git commit in worktree
│ git merge <branch> into original branch
│
▼
sandbox.cleanup() → CLEANED_UP
git worktree remove --force
git branch -D cleveragents/plan-<plan_id>
git worktree prune
At any point before commit(), calling rollback() resets the worktree
to the base commit and transitions to ROLLED_BACK. After commit(),
rollback() also resets the original branch to the pre-merge commit,
fully undoing the merge.
Key Classes
GitWorktreeSandbox
from cleveragents.infrastructure.sandbox.git_worktree import GitWorktreeSandbox
sandbox = GitWorktreeSandbox(
resource_id="my-repo",
original_path="/path/to/project",
git_timeout=30, # optional; default 30 s
)
ctx = sandbox.create(plan_id="01JXYZ")
worktree_file = sandbox.get_path("src/main.py")
# ... actor writes to worktree_file ...
result = sandbox.commit("feat: implement feature X")
sandbox.cleanup()
| Parameter | Type | Description |
|---|---|---|
resource_id |
str |
Identifier of the resource being sandboxed |
original_path |
str |
Absolute path to the git repository root |
git_timeout |
int |
Timeout in seconds for each git command (default: 30) |
SandboxContext
Returned by create(). Contains:
| Attribute | Type | Description |
|---|---|---|
sandbox_id |
str |
ULID uniquely identifying this sandbox instance |
sandbox_path |
str |
Absolute path to the worktree directory |
original_path |
str |
Absolute path to the original repository |
resource_id |
str |
Resource being sandboxed |
plan_id |
str |
Plan that owns this sandbox |
created_at |
datetime |
Creation timestamp |
metadata |
dict |
Extra info: strategy, branch, original_branch, base_commit, worktree_path |
CommitResult
Returned by commit(). Contains:
| Attribute | Type | Description |
|---|---|---|
sandbox_id |
str |
Sandbox identifier |
success |
bool |
Whether the commit and merge succeeded |
commit_ref |
str | None |
SHA of the commit in the worktree branch |
changed_files |
list[str] |
Modified files |
added_files |
list[str] |
New files |
deleted_files |
list[str] |
Deleted files |
error |
str | None |
Error message if success=False |
timestamp |
datetime |
Commit timestamp |
SandboxStatus
Lifecycle state machine:
PENDING → CREATED → ACTIVE → COMMITTED → CLEANED_UP
↘ ROLLED_BACK ↗
↘ ERRORED
- The worktree is removed:
git worktree remove <tmp_dir> --force. - The worktree branch is deleted:
git branch -d cleveragents/plan-<plan_id>.
Branch Naming
The sandbox branch is named cleveragents/plan-<sanitised_plan_id>.
The sanitiser replaces any character outside [a-zA-Z0-9/_.-] with a
hyphen and strips leading/trailing hyphens. For example:
| Plan ID | Branch |
|---|---|
01JXYZ |
cleveragents/plan-01JXYZ |
plan with spaces |
cleveragents/plan-plan-with-spaces |
# Review the conflict details
agents plan errors <plan_id>
## Error Handling
| Exception | When raised |
|-----------|-------------|
| `SandboxCreationError` | `git worktree add` fails or path is not a git root |
| `SandboxCommitError` | `git commit` or `git merge` fails |
| `SandboxRollbackError` | `git reset --hard` fails during rollback |
| `SandboxStateError` | Method called in an invalid lifecycle state |
| `ValueError` | Empty `resource_id`, `original_path`, or `plan_id`; path traversal attempt; `git_timeout <= 0` |
All git commands are wrapped with a configurable timeout.
`subprocess.TimeoutExpired` is caught and re-raised as the appropriate
`Sandbox*Error`.
## Context Hydration
## Rollback Behaviour
### Before `commit()` (ACTIVE → ROLLED_BACK)
Resets the worktree branch to the base commit and runs `git clean -fd` to
remove untracked files. The original branch is untouched.
### After `commit()` (COMMITTED → ROLLED_BACK)
1. Resets the **original branch** to the pre-merge commit (`git reset --hard <pre_merge_sha>`), undoing the merge.
2. Resets the **worktree branch** to the base commit.
> **Warning:** Rolling back from `COMMITTED` executes `git reset --hard`
> on the original branch. If other git worktrees or external processes
> track the same branch, they will be affected.
---
## Integration with Plan Lifecycle
`PlanLifecycleService` creates a `GitWorktreeSandbox` automatically when
the plan's linked resource is a `git-checkout` type:
```python
# Pseudocode — see PlanLifecycleService.execute_plan()
if resource.type == "git-checkout":
sandbox = GitWorktreeSandbox(resource.id, resource.path)
ctx = sandbox.create(plan.id)
# Actor writes to ctx.sandbox_path
result = sandbox.commit(f"plan({plan.id}): apply changes")
sandbox.cleanup()
The Apply Summary printed to the terminal includes:
- Plan ID
- Artifacts (added/changed/deleted file counts)
- Insertions and deletions (from
git diff --stat) - Project name and timestamp
- Sandbox cleanup confirmation
✓ OK Changes appliedfooter
Apply Summary Output
╭─ Apply Summary ──────────────────────────────────────────────────────────╮
│ Plan ID : 01JXYZ │
│ Project : my-project │
│ Timestamp : 2026-04-09 14:33:34 │
│ Artifacts : 3 added, 2 changed, 0 deleted │
│ Diff : +127 / -43 │
╰───────────────────────────────────────────────────────────────────────────╯
╭─ Sandbox Cleanup ─────────────────────────────────────────────────────────╮
│ ✓ Worktree removed │
│ ✓ Branch cleveragents/plan-01JXYZ deleted │
╰───────────────────────────────────────────────────────────────────────────╯
✓ OK Changes applied
Testing
The git worktree sandbox is covered by BDD scenarios in the following Behave feature files:
features/git_worktree_sandbox.feature— core lifecycle, path resolution, commit, rollback, cleanup, and error handlingfeatures/git_worktree_apply.feature— end-to-end apply flow viaPlanLifecycleServicefeatures/git_worktree_coverage_boost.feature— additional edge-case coverage for branch naming, timeout handling, and concurrent sandboxes
Example scenario from features/git_worktree_sandbox.feature:
Scenario: Create a git worktree sandbox
When a gwt sandbox is created for plan "plan-001"
Then the gwt sandbox should be in the "created" state
And the gwt sandbox context should reference plan "plan-001"
And the gwt sandbox context should have strategy metadata "git_worktree"
And the gwt sandbox worktree path should exist
Run the BDD suite with:
nox -s unit_tests
Related Documentation
- Architecture Overview — plan lifecycle and sandbox role
- ADR-015 Sandbox and Checkpoint — design rationale
- Shell Safety Module — another infrastructure safety component
- Invariant Reconciliation Module — phase-transition gating