Files
temp/docs/modules/git-worktree-sandbox.md
T
HAL9000 c29f755d1b docs: add git-worktree-sandbox and context-tier-hydration module guides
- Add docs/modules/git-worktree-sandbox.md: full guide for GitWorktreeSandbox
  including lifecycle, class reference, usage example, rollback semantics,
  apply phase panels, and error handling (PR #5998)
- Add docs/modules/context-tier-hydration.md: full guide for context tier
  hydration including API reference, file listing strategies, limits/filters,
  fragment metadata, and LLMExecuteActor integration (PR #4219)
- Update docs/architecture.md: add Sandbox System section covering
  GitWorktreeSandbox and context tier hydration
- Update CHANGELOG.md: add Fixed entries for plan use UNIQUE constraint
  violation (#4174) and duplicate execute dispatch (#2265)
- Update mkdocs.yml: add new module docs to navigation
2026-04-09 20:10:49 +00:00

7.6 KiB
Raw Blame History

Git Worktree Sandbox

The Git Worktree Sandbox provides isolated, branch-based staging for LLM-generated file changes during plan execution. It is the default sandbox strategy for resources of type git-checkout and replaces the earlier flat-directory shutil.copy2 approach for git-backed projects.

Implemented in PR #5998 (merged 2026-04-09). Spec reference: §1322513276.


Overview

When a plan enters the Execute phase for a git-checkout resource, the sandbox:

  1. Creates a new git branch cleveragents/plan-<plan_id> from the current HEAD of the repository.
  2. Adds a git worktree at a temporary directory pointing to that branch.
  3. Writes all LLM file output into the worktree (not the main working tree).
  4. On Apply, merges the worktree branch back into the project's current branch via git merge.
  5. Displays spec-aligned panels: Apply Summary, Sandbox Cleanup, and Next Steps.
  6. Removes the worktree and deletes the sandbox branch on cleanup.

Non-git projects fall back to the original flat-directory sandbox using shutil.copy2.


Architecture

graph LR
    subgraph "Execute Phase"
        LLM["LLM Actor"] -->|writes files| WT["Git Worktree\n(temp dir)"]
        WT -->|on branch| SB["cleveragents/plan-<id>"]
    end

    subgraph "Apply Phase"
        SB -->|git merge| MAIN["Project Branch\n(original HEAD)"]
        MAIN -->|cleanup| DEL["Worktree removed\nBranch deleted"]
    end

The worktree is completely isolated from the main working tree. Concurrent plans on the same repository each get their own branch and worktree directory, preventing interference.


Class Reference

GitWorktreeSandbox

from cleveragents.infrastructure.sandbox.git_worktree import GitWorktreeSandbox

Implements the Sandbox protocol.

Constructor

GitWorktreeSandbox(
    resource_id: str,
    original_path: str,
    git_timeout: int = 30,
)
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).

Raises: ValueError if any argument is invalid.

Lifecycle Methods

Method Description
create(plan_id) Creates the worktree and branch. Returns SandboxContext.
get_path(resource_path) Translates a resource-relative path to the worktree path.
commit(message=None) Stages all changes, commits in the worktree, and merges to the original branch.
rollback() Resets the worktree to the base commit; if already committed, also resets the original branch to the pre-merge commit.
cleanup() Removes the worktree and deletes the sandbox branch. Idempotent.

Status Transitions

PENDING → CREATED → ACTIVE → COMMITTED → CLEANED_UP
                  ↘ ROLLED_BACK ↗
                  ERRORED (any failure)

Usage Example

from cleveragents.infrastructure.sandbox.git_worktree import GitWorktreeSandbox

sandbox = GitWorktreeSandbox(
    resource_id="01HZ...",
    original_path="/home/user/my-project",
)

# Create the worktree
ctx = sandbox.create(plan_id="01HZ...")
print(ctx.sandbox_path)  # /tmp/ca-sandbox-01HZ...-xxxxx

# Resolve a path inside the worktree
worktree_file = sandbox.get_path("src/main.py")

# Write files (done by LLM actor)
with open(worktree_file, "w") as f:
    f.write("# LLM-generated content\n")

# Commit and merge back
result = sandbox.commit("feat: LLM-generated changes")
print(result.commit_ref)      # git commit hash
print(result.added_files)     # ["src/main.py"]
print(result.changed_files)   # []

# Cleanup
sandbox.cleanup()

Branch Naming

Sandbox branches follow the pattern:

cleveragents/plan-<sanitised_plan_id>

The plan ID is sanitised to be git-safe: only alphanumerics, hyphens, underscores, slashes, and dots are kept; runs of other characters are collapsed to a single hyphen.


Rollback Semantics

rollback() supports two scenarios:

Status at rollback Behaviour
ACTIVE Resets the worktree branch to the base commit and cleans untracked files.
COMMITTED Also resets the original branch to the pre-merge commit (git reset --hard), fully undoing the merge.

!!! warning "Multi-worktree safety" Rolling back from COMMITTED executes git reset --hard on the original branch. If other git worktrees or external processes are tracking the same branch, they will be affected.


Apply Phase Panels

When plan apply completes for a git-checkout resource, the CLI renders three spec-aligned panels:

=== "Apply Summary" ┌─ Apply Summary ──────────────────────────────────────────────────┐ │ Plan ID: 01HZ... │ │ Artifacts: 3 files │ │ Changes: +42 / -7 │ │ Project: local/my-project │ │ Applied at: 2026-04-09 14:33:34 │ └──────────────────────────────────────────────────────────────────┘

=== "Sandbox Cleanup" ┌─ Sandbox Cleanup ────────────────────────────────────────────────┐ │ ✓ Worktree removed │ │ ✓ Branch merged to main │ └──────────────────────────────────────────────────────────────────┘

=== "Next Steps" ┌─ Next Steps ─────────────────────────────────────────────────────┐ │ 1. Review changes: git diff HEAD~1 │ │ 2. Commit if satisfied: git commit --amend │ └──────────────────────────────────────────────────────────────────┘ ✓ OK Changes applied


Error Handling

All git operations are wrapped with:

  • Timeout: Each command has a configurable timeout (default 30 s). subprocess.TimeoutExpired raises SandboxCreationError, SandboxCommitError, or SandboxRollbackError as appropriate.
  • Non-zero exit: subprocess.CalledProcessError is caught and re-raised as the appropriate sandbox error with the git stderr included.
  • Cleanup fallback: If git worktree remove fails, the directory is removed manually with shutil.rmtree.