docs: git worktree sandbox guide, extend sandbox/plan-apply/context-tiers refs (Cycle 9) #7378
@@ -0,0 +1,147 @@
|
||||
# Git Worktree Sandbox
|
||||
|
||||
The **Git Worktree Sandbox** provides git-native isolation for plan execution
|
||||
on `git-checkout` resources. Introduced in v3.5.0 (PR #5998), it replaces
|
||||
the previous flat `shutil.copy2` apply with a proper `git merge` workflow,
|
||||
giving users a clean git history and conflict-detection on apply.
|
||||
|
||||
## Why Git Worktrees?
|
||||
|
||||
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 merge` surfaces conflicts that flat file copy
|
||||
would silently overwrite.
|
||||
- **Rollback**: The worktree branch can be deleted without affecting the
|
||||
main branch.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
agents plan execute <plan_id>
|
||||
│
|
||||
▼
|
||||
GitWorktreeSandbox.create()
|
||||
│
|
||||
├── git worktree add /tmp/ca-wt-<id> -b cleveragents/plan-<plan_id>
|
||||
│
|
||||
▼
|
||||
LLMExecuteActor writes files to worktree path
|
||||
│
|
||||
▼
|
||||
git commit -m "plan: <plan_id> execute output" (inside worktree)
|
||||
│
|
||||
▼
|
||||
sandbox_ref stored → plan record
|
||||
|
||||
agents plan apply <plan_id>
|
||||
│
|
||||
▼
|
||||
GitWorktreeSandbox.commit()
|
||||
│
|
||||
├── git merge --no-ff cleveragents/plan-<plan_id>
|
||||
│
|
||||
├── Display Apply Summary panel
|
||||
├── Display Sandbox Cleanup panel
|
||||
├── Display Next Steps panel
|
||||
└── ✓ OK Changes applied
|
||||
```
|
||||
|
||||
## Execute Phase
|
||||
|
||||
When `agents plan execute <plan_id>` runs for a plan linked to a
|
||||
`git-checkout` resource:
|
||||
|
||||
1. `GitWorktreeSandbox.create()` is called with the resource's root path.
|
||||
2. A temporary directory is created (e.g. `/tmp/ca-wt-<ulid>`).
|
||||
3. `git worktree add <tmp_dir> -b cleveragents/plan-<plan_id>` creates the
|
||||
isolated branch.
|
||||
4. The `LLMExecuteActor` writes all generated files into `<tmp_dir>`.
|
||||
5. Changes are committed: `git commit -m "plan: <plan_id> execute output"`.
|
||||
6. The worktree path is stored as a sandbox reference in the plan record.
|
||||
|
||||
## Apply Phase
|
||||
|
||||
When `agents plan apply <plan_id>` runs:
|
||||
|
||||
1. The sandbox reference is resolved to the worktree path.
|
||||
2. `git merge --no-ff cleveragents/plan-<plan_id>` is run in the main
|
||||
repository.
|
||||
3. On success, the CLI renders:
|
||||
|
||||
```
|
||||
╭─ Apply Summary ─────────────────────────────────────────────╮
|
||||
│ Plan: 01HXYZ... │
|
||||
│ Artifacts: 3 files │
|
||||
│ Changes: +42 / -7 │
|
||||
│ Project: my-project │
|
||||
│ Applied at: 2026-04-10T14:23:00Z │
|
||||
╰─────────────────────────────────────────────────────────────╯
|
||||
╭─ Sandbox Cleanup ───────────────────────────────────────────╮
|
||||
│ ✓ Worktree removed │
|
||||
│ ✓ Branch merged to main │
|
||||
╰─────────────────────────────────────────────────────────────╯
|
||||
╭─ Next Steps ────────────────────────────────────────────────╮
|
||||
│ Review changes: git diff HEAD~1 │
|
||||
│ Commit if satisfied: git commit --amend │
|
||||
╰─────────────────────────────────────────────────────────────╯
|
||||
✓ OK Changes applied
|
||||
```
|
||||
|
||||
4. The worktree is removed: `git worktree remove <tmp_dir> --force`.
|
||||
5. The worktree branch is deleted: `git branch -d cleveragents/plan-<plan_id>`.
|
||||
|
||||
## Non-Git Fallback
|
||||
|
||||
If the linked resource is not a git repository (e.g. a plain directory
|
||||
resource), the sandbox falls back to `CopyOnWriteSandbox`:
|
||||
|
||||
- Files are copied from the sandbox directory to the project directory using
|
||||
`shutil.copy2`.
|
||||
- Path traversal guards prevent writes outside the project root.
|
||||
- Protected directories (`.git`, `.cleveragents`, `node_modules`,
|
||||
`__pycache__`) are skipped.
|
||||
|
||||
## Merge Conflict Handling
|
||||
|
||||
If `git merge` encounters conflicts:
|
||||
|
||||
1. The plan transitions to `ERRORED` processing state.
|
||||
2. `error_message` is set to `"Merge failed: <details>"`.
|
||||
3. `error_details` includes `merge_conflict` and `sandbox_rollback: "pending"`.
|
||||
|
||||
**Recovery steps:**
|
||||
|
||||
```bash
|
||||
# Review the conflict details
|
||||
agents plan errors <plan_id>
|
||||
|
||||
# Manually resolve conflicts in the worktree branch
|
||||
git checkout cleveragents/plan-<plan_id>
|
||||
# ... resolve conflicts ...
|
||||
git commit -m "resolve: manual conflict resolution"
|
||||
|
||||
# Re-apply
|
||||
agents plan apply <plan_id>
|
||||
```
|
||||
|
||||
## Context Hydration
|
||||
|
||||
Before the LLM writes files to the worktree, the `ContextTierHydrator`
|
||||
pre-populates the `ContextTierService` with the project's source files.
|
||||
This ensures the LLM has full file context during execution.
|
||||
|
||||
See [Context Tiers Reference](../reference/context_tiers.md) for details on
|
||||
the hydration pipeline.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Sandbox Infrastructure Reference](../reference/sandbox.md)
|
||||
- [Plan Diff & Apply Reference](../reference/plan_apply.md)
|
||||
- [Context Tiers Reference](../reference/context_tiers.md)
|
||||
- [ADR-015: Sandbox & Checkpoint](../adr/ADR-015-sandbox-and-checkpoint.md)
|
||||
@@ -107,3 +107,58 @@ from cleveragents.application.container import get_container
|
||||
container = get_container()
|
||||
svc = container.context_tier_service()
|
||||
```
|
||||
|
||||
## Context Tier Hydration
|
||||
|
||||
Prior to v3.4.0 (fix in PR #4219), the `ContextTierService` started empty on
|
||||
every CLI invocation, causing the LLM to receive zero file context during plan
|
||||
execution. The **context tier hydrator** resolves this by pre-populating the
|
||||
tier service from linked project resources before context assembly runs.
|
||||
|
||||
### How Hydration Works
|
||||
|
||||
The `ContextTierHydrator` is invoked automatically inside
|
||||
`LLMExecuteActor.execute()` before context assembly:
|
||||
|
||||
1. Resolves the plan's linked project resources via `ProjectRepository`.
|
||||
2. For each `git-checkout` resource: runs `git ls-files` to enumerate tracked
|
||||
files.
|
||||
3. For other directory resources: walks the filesystem with `os.walk`.
|
||||
4. Reads each file (up to `max_file_size`, default **256 KB**) and creates a
|
||||
`TieredFragment` in the **hot** tier.
|
||||
5. Stops when the total budget (`max_total_size`, default **10 MB**) is
|
||||
reached.
|
||||
|
||||
### Exclusion Rules
|
||||
|
||||
The hydrator skips:
|
||||
|
||||
- Binary files (detected by null-byte scan)
|
||||
- Files exceeding `max_file_size` (256 KB)
|
||||
- Directories: `.git`, `node_modules`, `__pycache__`, `.cleveragents`
|
||||
|
||||
### Configuration
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `max_file_size` | 262144 (256 KB) | Maximum size of a single file to hydrate |
|
||||
| `max_total_size` | 10485760 (10 MB) | Total budget across all hydrated files |
|
||||
|
||||
### Integration with LLMExecuteActor
|
||||
|
||||
```python
|
||||
# Pseudocode — hydration is automatic, no manual call needed
|
||||
class LLMExecuteActor:
|
||||
def execute(self, plan_id, ...):
|
||||
hydrator = ContextTierHydrator(
|
||||
tier_service=self.tier_service,
|
||||
project_repository=self.project_repository,
|
||||
resource_registry=self.resource_registry,
|
||||
)
|
||||
hydrator.hydrate(plan_id) # populates tier service
|
||||
context = self.assembler.assemble(...) # now has file context
|
||||
...
|
||||
```
|
||||
|
||||
The hydrator is injected via constructor — no `get_container()` call is made
|
||||
inside the actor, keeping it testable and DI-compliant.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Plan Diff & Apply Reference
|
||||
|
||||
This document describes the diff review and apply integration features
|
||||
added in D0b.apply.
|
||||
This document describes the diff review and apply integration features,
|
||||
including the git worktree merge-based apply workflow introduced in v3.5.0
|
||||
(PR #5998).
|
||||
|
||||
## CLI Commands
|
||||
|
||||
@@ -66,6 +67,39 @@ change list, and validation results.
|
||||
|
||||
## Apply Integration
|
||||
|
||||
### Git Worktree Merge-Based Apply
|
||||
|
||||
For plans linked to a git-checkout resource, `agents plan apply` uses a
|
||||
git-native merge workflow instead of flat file copying:
|
||||
|
||||
1. **Execute phase** writes LLM output to an isolated git worktree branch
|
||||
`cleveragents/plan-<plan_id>` (see [Sandbox Infrastructure](sandbox.md)).
|
||||
2. **Apply phase** runs `git merge --no-ff` to merge the worktree branch
|
||||
into the project's current branch.
|
||||
3. The CLI displays spec-aligned output panels:
|
||||
|
||||
```
|
||||
╭─ Apply Summary ─────────────────────────────────────────────╮
|
||||
│ Plan: 01HXYZ... │
|
||||
│ Artifacts: 3 files │
|
||||
│ Changes: +42 / -7 │
|
||||
│ Project: my-project │
|
||||
│ Applied at: 2026-04-10T14:23:00Z │
|
||||
╰─────────────────────────────────────────────────────────────╯
|
||||
╭─ Sandbox Cleanup ───────────────────────────────────────────╮
|
||||
│ ✓ Worktree removed │
|
||||
│ ✓ Branch merged to main │
|
||||
╰─────────────────────────────────────────────────────────────╯
|
||||
╭─ Next Steps ────────────────────────────────────────────────╮
|
||||
│ Review changes: git diff HEAD~1 │
|
||||
│ Commit if satisfied: git commit --amend │
|
||||
╰─────────────────────────────────────────────────────────────╯
|
||||
✓ OK Changes applied
|
||||
```
|
||||
|
||||
For non-git projects, apply falls back to flat file copy (`shutil.copy2`)
|
||||
from the sandbox directory to the project directory.
|
||||
|
||||
### Empty ChangeSet Guard
|
||||
|
||||
The apply pipeline checks whether the ChangeSet has any entries before
|
||||
|
||||
@@ -15,6 +15,55 @@ committed.
|
||||
| `copy_on_write` | `CopyOnWriteSandbox` | Filesystem copy for isolation |
|
||||
| `git_worktree` | `GitWorktreeSandbox` | Git worktree for git repositories |
|
||||
|
||||
### Strategy Selection
|
||||
|
||||
The sandbox strategy is chosen automatically based on the resource type:
|
||||
|
||||
- **Git repositories** (`git-checkout` resources): `git_worktree` strategy is used.
|
||||
The plan's changes are committed to an isolated branch
|
||||
`cleveragents/plan-<plan_id>` inside a git worktree, keeping the working
|
||||
tree clean until apply.
|
||||
- **Non-git directories**: `copy_on_write` strategy is used. The resource
|
||||
directory is copied to a temporary location; changes are written there and
|
||||
copied back on apply.
|
||||
- **No isolation needed**: `none` strategy writes directly to the resource.
|
||||
|
||||
## Git Worktree Sandbox
|
||||
|
||||
The `GitWorktreeSandbox` provides full git-native isolation for plan execution
|
||||
on git-checkout resources. It was introduced in v3.5.0 (PR #5998) to replace
|
||||
flat `shutil.copy2` apply with a proper `git merge` workflow.
|
||||
|
||||
### Execute Phase
|
||||
|
||||
1. A git worktree is created at a temporary path for the plan's linked
|
||||
git-checkout resource.
|
||||
2. A new branch `cleveragents/plan-<plan_id>` is checked out inside the
|
||||
worktree.
|
||||
3. LLM-generated file output is written to the worktree directory.
|
||||
4. Changes are committed to the worktree branch — **no merge yet**.
|
||||
5. The worktree path is stored as a sandbox reference for the apply phase.
|
||||
|
||||
### Apply Phase
|
||||
|
||||
1. The worktree branch is merged into the project's current branch via
|
||||
`git merge --no-ff`.
|
||||
2. The CLI displays spec-aligned output panels:
|
||||
- **Apply Summary**: Plan ID, artifact count, insertions/deletions,
|
||||
project name, applied-at timestamp.
|
||||
- **Sandbox Cleanup**: worktree removed, branch merged to main.
|
||||
- **Next Steps**: review `git diff`, commit changes.
|
||||
- **Footer**: `✓ OK Changes applied`
|
||||
3. The worktree is removed after a successful merge.
|
||||
|
||||
### Non-Git Fallback
|
||||
|
||||
If the linked resource is not a git repository, the sandbox falls back to
|
||||
the `copy_on_write` strategy: files are copied from the sandbox directory
|
||||
to the project directory using `shutil.copy2`. Path traversal guards
|
||||
prevent writes outside the project root. Protected directories (`.git`,
|
||||
`.cleveragents`, `node_modules`) are skipped.
|
||||
|
||||
## Sandbox Lifecycle
|
||||
|
||||
```
|
||||
|
||||
@@ -27,6 +27,7 @@ nav:
|
||||
- Shell Safety: modules/shell-safety.md
|
||||
- UKO Provenance Tracking: modules/uko-provenance.md
|
||||
- Invariant Reconciliation: modules/invariant-reconciliation.md
|
||||
- Git Worktree Sandbox: modules/git-worktree-sandbox.md
|
||||
- Development:
|
||||
- CI/CD Pipeline: development/ci-cd.md
|
||||
- Quality Automation: development/quality-automation.md
|
||||
|
||||
Reference in New Issue
Block a user