diff --git a/docs/modules/git-worktree-sandbox.md b/docs/modules/git-worktree-sandbox.md new file mode 100644 index 000000000..b7f1a8288 --- /dev/null +++ b/docs/modules/git-worktree-sandbox.md @@ -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-`) 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 + │ + ▼ + GitWorktreeSandbox.create() + │ + ├── git worktree add /tmp/ca-wt- -b cleveragents/plan- + │ + ▼ + LLMExecuteActor writes files to worktree path + │ + ▼ + git commit -m "plan: execute output" (inside worktree) + │ + ▼ + sandbox_ref stored → plan record + +agents plan apply + │ + ▼ + GitWorktreeSandbox.commit() + │ + ├── git merge --no-ff cleveragents/plan- + │ + ├── Display Apply Summary panel + ├── Display Sandbox Cleanup panel + ├── Display Next Steps panel + └── ✓ OK Changes applied +``` + +## Execute Phase + +When `agents plan execute ` 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-`). +3. `git worktree add -b cleveragents/plan-` creates the + isolated branch. +4. The `LLMExecuteActor` writes all generated files into ``. +5. Changes are committed: `git commit -m "plan: execute output"`. +6. The worktree path is stored as a sandbox reference in the plan record. + +## Apply Phase + +When `agents plan apply ` runs: + +1. The sandbox reference is resolved to the worktree path. +2. `git merge --no-ff cleveragents/plan-` 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 --force`. +5. The worktree branch is deleted: `git branch -d cleveragents/plan-`. + +## 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:
"`. +3. `error_details` includes `merge_conflict` and `sandbox_rollback: "pending"`. + +**Recovery steps:** + +```bash +# Review the conflict details +agents plan errors + +# Manually resolve conflicts in the worktree branch +git checkout cleveragents/plan- +# ... resolve conflicts ... +git commit -m "resolve: manual conflict resolution" + +# Re-apply +agents plan apply +``` + +## 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) diff --git a/docs/reference/context_tiers.md b/docs/reference/context_tiers.md index 488efb0e9..5594cc332 100644 --- a/docs/reference/context_tiers.md +++ b/docs/reference/context_tiers.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. diff --git a/docs/reference/plan_apply.md b/docs/reference/plan_apply.md index b53ea8045..4c15fda04 100644 --- a/docs/reference/plan_apply.md +++ b/docs/reference/plan_apply.md @@ -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-` (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 diff --git a/docs/reference/sandbox.md b/docs/reference/sandbox.md index c8089f0d1..808ae66e3 100644 --- a/docs/reference/sandbox.md +++ b/docs/reference/sandbox.md @@ -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-` 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-` 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 ``` diff --git a/mkdocs.yml b/mkdocs.yml index af4eba30e..197f1c704 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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