docs(modules): add git worktree sandbox module documentation
CI / lint (pull_request) Successful in 35s
CI / quality (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 55s
CI / security (pull_request) Successful in 47s
CI / build (pull_request) Successful in 35s
CI / helm (pull_request) Successful in 36s
CI / push-validation (pull_request) Successful in 24s
CI / integration_tests (pull_request) Successful in 4m29s
CI / e2e_tests (pull_request) Successful in 4m27s
CI / unit_tests (pull_request) Successful in 5m38s
CI / docker (pull_request) Successful in 24s
CI / coverage (pull_request) Successful in 12m27s
CI / status-check (pull_request) Successful in 2s
CI / lint (pull_request) Successful in 35s
CI / quality (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 55s
CI / security (pull_request) Successful in 47s
CI / build (pull_request) Successful in 35s
CI / helm (pull_request) Successful in 36s
CI / push-validation (pull_request) Successful in 24s
CI / integration_tests (pull_request) Successful in 4m29s
CI / e2e_tests (pull_request) Successful in 4m27s
CI / unit_tests (pull_request) Successful in 5m38s
CI / docker (pull_request) Successful in 24s
CI / coverage (pull_request) Successful in 12m27s
CI / status-check (pull_request) Successful in 2s
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
This commit is contained in:
@@ -17,6 +17,15 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
### Added
|
||||
|
||||
- **Git Worktree Sandbox Module Documentation** (#8051): Added comprehensive
|
||||
module reference page `docs/modules/git-worktree-sandbox.md` covering the
|
||||
`GitWorktreeSandbox` class introduced in v3.5.0 (PR #5998). Documents the
|
||||
lifecycle state machine, key classes (`GitWorktreeSandbox`, `SandboxContext`,
|
||||
`CommitResult`, `SandboxStatus`), branch naming convention, error handling
|
||||
table, rollback behaviour (pre/post-commit), integration with
|
||||
`PlanLifecycleService`, Apply Summary output format, and BDD testing guidance
|
||||
referencing `features/git_worktree_sandbox.feature`.
|
||||
|
||||
- **TDD Issue-Capture Test Activation** (#7025): Replaced 234 bare `@skip` tags
|
||||
across 82 Behave feature files with the correct `@tdd_expected_fail @tdd_issue
|
||||
@tdd_issue_<N>` tag system. Scenarios whose referenced bugs were already fixed
|
||||
|
||||
@@ -16,4 +16,5 @@ Below are some of the specific details of various contributions.
|
||||
* Brent E. Edwards has contributed quality assurance, test coverage, and CI pipeline improvements.
|
||||
* HAL 9000 has contributed automated implementation, bug fixes, and feature development as part of the CleverAgents automation pool.
|
||||
* HAL 9000 has contributed the plan concurrency race-condition fix (#7989): wired `LockService` into the plan lifecycle, guarding `execute_plan()` and `apply_plan()` with plan-level advisory locks and unique per-invocation owner identities to prevent silent concurrent state corruption.
|
||||
* HAL 9000 has contributed the Git Worktree Sandbox module documentation (#8051): comprehensive module reference page covering lifecycle state machine, key classes, branch naming, error handling, rollback behaviour, and BDD testing guidance for the `GitWorktreeSandbox` introduced in v3.5.0.
|
||||
* This project was made possible thanks to considerable donation of time, money, and resources by CleverThis, Inc.
|
||||
|
||||
@@ -1,190 +1,265 @@
|
||||
# Git Worktree Sandbox
|
||||
# Git Worktree Sandbox Module
|
||||
|
||||
**Package:** `cleveragents.infrastructure.sandbox.git_worktree`
|
||||
**Introduced:** Unreleased (issue #4454)
|
||||
**Package:** `cleveragents.infrastructure.sandbox`
|
||||
**Introduced:** v3.5.0 (PR #5998)
|
||||
|
||||
The git worktree sandbox isolates plan modifications in a dedicated git
|
||||
branch and worktree. On commit, changes are merged back to the original
|
||||
branch via `git merge`. On rollback, the worktree is discarded entirely.
|
||||
This replaces the previous flat `shutil.copy2` apply strategy for
|
||||
git-checkout resources.
|
||||
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`](../api/core.md).
|
||||
For the plan lifecycle that invokes the sandbox, see
|
||||
[`docs/architecture.md`](../architecture.md#plan-lifecycle).
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
When a plan applies changes to a git-checkout resource, the changes must
|
||||
be isolated from the working tree until the user approves them. The git
|
||||
worktree sandbox achieves this by:
|
||||
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:
|
||||
|
||||
1. Creating a new branch `cleveragents/plan-<plan_id>` from the current HEAD.
|
||||
2. Creating a temporary git worktree at a system temp directory.
|
||||
3. Letting the actor write changes inside the worktree.
|
||||
4. On commit: staging all changes, committing them in the worktree, then
|
||||
merging the sandbox branch back into the original branch.
|
||||
5. On rollback: resetting the worktree (and optionally the original branch)
|
||||
to the base commit and discarding the worktree.
|
||||
- The original working tree is never modified until `plan apply` is 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 file copy strategy.
|
||||
|
||||
---
|
||||
|
||||
## `GitWorktreeSandbox`
|
||||
|
||||
```python
|
||||
from cleveragents.infrastructure.sandbox.git_worktree import GitWorktreeSandbox
|
||||
|
||||
sandbox = GitWorktreeSandbox(
|
||||
resource_id="01HZ...",
|
||||
original_path="/path/to/repo",
|
||||
)
|
||||
```
|
||||
|
||||
Implements the `Sandbox` protocol.
|
||||
|
||||
**Constructor parameters:**
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `resource_id` | `str` | — | Identifier of the resource being sandboxed |
|
||||
| `original_path` | `str` | — | Path to the git repository root |
|
||||
| `git_timeout` | `int` | `30` | Timeout in seconds for git commands |
|
||||
Non-git projects fall back to the original flat `shutil.copy2` strategy.
|
||||
|
||||
---
|
||||
|
||||
## Lifecycle
|
||||
|
||||
```
|
||||
PENDING ──► create() ──► CREATED ──► get_path() ──► ACTIVE
|
||||
│
|
||||
commit() ─┤─► COMMITTED ──► cleanup() ──► CLEANED_UP
|
||||
│
|
||||
rollback() ─┴─► ROLLED_BACK ──► cleanup() ──► CLEANED_UP
|
||||
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
|
||||
```
|
||||
|
||||
### `create(plan_id) → SandboxContext`
|
||||
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.
|
||||
|
||||
Creates the worktree and branch.
|
||||
---
|
||||
|
||||
## Key Classes
|
||||
|
||||
### `GitWorktreeSandbox`
|
||||
|
||||
```python
|
||||
ctx = sandbox.create(plan_id="plan-01HZ...")
|
||||
# ctx.sandbox_path — path to the worktree
|
||||
# ctx.metadata["branch"] — e.g. "cleveragents/plan-plan-01HZ..."
|
||||
# ctx.metadata["base_commit"] — HEAD SHA at creation time
|
||||
```
|
||||
from cleveragents.infrastructure.sandbox.git_worktree import GitWorktreeSandbox
|
||||
|
||||
Raises `SandboxCreationError` if the path is not a git repository root or
|
||||
if `git worktree add` fails.
|
||||
|
||||
### `get_path(resource_path) → str`
|
||||
|
||||
Translates a resource-relative path to an absolute path inside the
|
||||
worktree. Rejects `..` path traversal attempts.
|
||||
|
||||
```python
|
||||
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")
|
||||
# → "/tmp/ca-sandbox-plan-01HZ.../src/main.py"
|
||||
# ... actor writes to worktree_file ...
|
||||
result = sandbox.commit("feat: implement feature X")
|
||||
sandbox.cleanup()
|
||||
```
|
||||
|
||||
### `commit(message=None) → CommitResult`
|
||||
| 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) |
|
||||
|
||||
Stages all changes in the worktree, commits them, then merges the sandbox
|
||||
branch into the original branch.
|
||||
### `SandboxContext`
|
||||
|
||||
```python
|
||||
result = sandbox.commit("feat: implement new feature")
|
||||
# result.success — True on success
|
||||
# result.commit_ref — SHA of the sandbox commit
|
||||
# result.changed_files — list of modified file paths
|
||||
# result.added_files — list of newly added file paths
|
||||
# result.deleted_files — list of deleted file paths
|
||||
```
|
||||
Returned by `create()`. Contains:
|
||||
|
||||
If there are no staged changes, returns a `CommitResult` with empty file
|
||||
lists and no new commit.
|
||||
| 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` |
|
||||
|
||||
Raises `SandboxCommitError` if the commit or merge fails.
|
||||
### `CommitResult`
|
||||
|
||||
### `rollback()`
|
||||
Returned by `commit()`. Contains:
|
||||
|
||||
Discards all worktree changes.
|
||||
| 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 |
|
||||
|
||||
- From `ACTIVE`: resets the worktree branch to the base commit.
|
||||
- From `COMMITTED`: also resets the original branch to the pre-merge commit
|
||||
(fully undoes the merge).
|
||||
### `SandboxStatus`
|
||||
|
||||
!!! 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.
|
||||
|
||||
### `cleanup()`
|
||||
|
||||
Removes the worktree directory and deletes the sandbox branch. Idempotent
|
||||
— safe to call multiple times.
|
||||
|
||||
---
|
||||
|
||||
## Apply Summary
|
||||
|
||||
When `plan apply` uses the git worktree strategy, the CLI displays a
|
||||
spec-aligned Apply Summary:
|
||||
Lifecycle state machine:
|
||||
|
||||
```
|
||||
╭─ Apply Summary ──────────────────────────────────────╮
|
||||
│ Plan ID: plan-01HZ... │
|
||||
│ Artifacts: 3 files changed │
|
||||
│ Insertions: +142 │
|
||||
│ Deletions: -17 │
|
||||
│ Project: local/my-project │
|
||||
│ Timestamp: 2026-04-09 17:30:00 │
|
||||
╰──────────────────────────────────────────────────────╯
|
||||
╭─ Sandbox Cleanup ────────────────────────────────────╮
|
||||
│ ✓ Worktree removed │
|
||||
│ ✓ Branch deleted │
|
||||
╰──────────────────────────────────────────────────────╯
|
||||
✓ OK Changes applied
|
||||
PENDING → CREATED → ACTIVE → COMMITTED → CLEANED_UP
|
||||
↘ ROLLED_BACK ↗
|
||||
↘ ERRORED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Types
|
||||
|
||||
| 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 |
|
||||
|
||||
All exceptions are importable from
|
||||
`cleveragents.infrastructure.sandbox.protocol`.
|
||||
|
||||
---
|
||||
|
||||
## Branch Naming
|
||||
|
||||
Branch names are sanitised from the plan ID:
|
||||
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:
|
||||
|
||||
- Only alphanumeric characters, hyphens, underscores, slashes, and dots
|
||||
are kept.
|
||||
- Runs of disallowed characters are collapsed into a single hyphen.
|
||||
- The final branch name has the form `cleveragents/plan-<sanitised_plan_id>`.
|
||||
| Plan ID | Branch |
|
||||
|---------|--------|
|
||||
| `01JXYZ` | `cleveragents/plan-01JXYZ` |
|
||||
| `plan with spaces` | `cleveragents/plan-plan-with-spaces` |
|
||||
|
||||
---
|
||||
|
||||
## Fallback for Non-Git Projects
|
||||
## Error Handling
|
||||
|
||||
`PlanLifecycleService` detects whether the resource is a git-checkout type.
|
||||
If not, it falls back to the original flat file copy strategy
|
||||
(`shutil.copy2`). The git worktree sandbox is only used for resources
|
||||
where `resource_type_name` is `git-checkout` or `git`.
|
||||
| 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`.
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
## Rollback Behaviour
|
||||
|
||||
- [Architecture — Plan Lifecycle](../architecture.md#plan-lifecycle)
|
||||
- [ADR-015 Sandbox & Checkpoint](../adr/ADR-015-sandbox-and-checkpoint.md)
|
||||
- [ADR-038 Cross-Mechanism Sandbox Coordination](../adr/ADR-038-cross-mechanism-sandbox-coordination.md)
|
||||
- [`cleveragents.infrastructure.sandbox`](../api/core.md)
|
||||
### 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 applied` footer
|
||||
|
||||
---
|
||||
|
||||
## 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 handling
|
||||
- `features/git_worktree_apply.feature` — end-to-end apply flow via
|
||||
`PlanLifecycleService`
|
||||
- `features/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`:
|
||||
|
||||
```gherkin
|
||||
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](../architecture.md) — plan lifecycle and sandbox role
|
||||
- [ADR-015 Sandbox and Checkpoint](../adr/ADR-015-sandbox-and-checkpoint.md) — design rationale
|
||||
- [Shell Safety Module](shell-safety.md) — another infrastructure safety component
|
||||
- [Invariant Reconciliation Module](invariant-reconciliation.md) — phase-transition gating
|
||||
|
||||
Reference in New Issue
Block a user