0ca1303927
CI / lint (pull_request) Successful in 23s
CI / quality (pull_request) Successful in 29s
CI / security (pull_request) Successful in 54s
CI / typecheck (pull_request) Successful in 59s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 25s
CI / integration_tests (pull_request) Successful in 4m33s
CI / benchmark-regression (pull_request) Successful in 22m32s
CI / unit_tests (pull_request) Successful in 30m28s
CI / docker (pull_request) Successful in 39s
CI / coverage (pull_request) Successful in 1h39m42s
CI / lint (push) Successful in 12s
CI / quality (push) Successful in 20s
CI / build (push) Successful in 24s
CI / security (push) Successful in 28s
CI / typecheck (push) Successful in 1m0s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 4m35s
CI / benchmark-publish (push) Successful in 13m36s
CI / coverage (push) Has been cancelled
CI / unit_tests (push) Has been cancelled
CI / docker (push) Has been cancelled
Introduce a lightweight checkpoint/rollback system for sandbox state during plan execute and apply flows. CheckpointManager snapshots the sandbox working directory before each phase and can restore it on failure, giving the execution engine a reliable undo mechanism. Key changes: - SandboxCheckpoint model, Checkpointable protocol, and CheckpointManager in infrastructure/sandbox/checkpoint.py - PlanExecutor gains optional checkpoint_manager with pre/post execute hooks and automatic rollback on failure - PlanApplyService gains optional checkpoint_manager with pre-apply checkpoint and rollback helper - 12 BDD scenarios (features/sandbox_checkpoints.feature) - 5 Robot Framework smoke tests (robot/sandbox_checkpoint_smoke.robot) - ASV benchmarks for creation, rollback, and listing operations - Reference documentation in docs/reference/sandbox.md ISSUES CLOSED: #183
118 lines
3.9 KiB
Markdown
118 lines
3.9 KiB
Markdown
# Sandbox Infrastructure
|
|
|
|
## Overview
|
|
|
|
The sandbox infrastructure provides resource isolation during plan execution.
|
|
Each sandbox creates an isolated environment where a plan can read and write
|
|
to a resource without affecting the original until changes are explicitly
|
|
committed.
|
|
|
|
## Sandbox Strategies
|
|
|
|
| Strategy | Class | Description |
|
|
|----------|-------|-------------|
|
|
| `none` | `NoSandbox` | No isolation; writes go directly to the original |
|
|
| `copy_on_write` | `CopyOnWriteSandbox` | Filesystem copy for isolation |
|
|
| `git_worktree` | `GitWorktreeSandbox` | Git worktree for git repositories |
|
|
|
|
## Sandbox Lifecycle
|
|
|
|
```
|
|
PENDING -> CREATED -> ACTIVE -> COMMITTED -> CLEANED_UP
|
|
| |
|
|
| +-> CLEANED_UP
|
|
|
|
|
+-> ROLLED_BACK -> ACTIVE
|
|
|
|
|
+-> ERRORED -> CLEANED_UP
|
|
```
|
|
|
|
## Checkpoint and Rollback Hooks
|
|
|
|
### Purpose
|
|
|
|
Checkpoint hooks preserve sandbox state at key points during plan
|
|
execute/apply flows. When a failure occurs, the `CheckpointManager`
|
|
can restore a sandbox to a previously captured checkpoint.
|
|
|
|
### SandboxCheckpoint Model
|
|
|
|
A frozen Pydantic model capturing a point-in-time snapshot:
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `checkpoint_id` | `str` | ULID identifier |
|
|
| `sandbox_id` | `str` | Sandbox that was checkpointed |
|
|
| `plan_id` | `str` | Plan owning the sandbox |
|
|
| `phase` | `str` | Lifecycle phase (`pre_execute`, `post_execute`, `pre_apply`) |
|
|
| `created_at` | `datetime` | When captured |
|
|
| `metadata` | `dict[str, str]` | Key-value metadata (status, reason, etc.) |
|
|
| `snapshot_path` | `str` | Path to the snapshot directory |
|
|
|
|
### CheckpointManager API
|
|
|
|
```python
|
|
mgr = CheckpointManager()
|
|
|
|
# Create a snapshot before execute
|
|
cp = mgr.create_checkpoint(sandbox, plan_id, "pre_execute", {})
|
|
|
|
# Create a snapshot after successful execute
|
|
cp2 = mgr.create_checkpoint(sandbox, plan_id, "post_execute", {"status": "success"})
|
|
|
|
# Rollback on failure
|
|
success = mgr.rollback_to(cp)
|
|
|
|
# List all checkpoints for a sandbox
|
|
checkpoints = mgr.list_checkpoints(sandbox.sandbox_id)
|
|
|
|
# Delete a checkpoint
|
|
mgr.delete_checkpoint(cp.checkpoint_id)
|
|
```
|
|
|
|
### Checkpoint Lifecycle
|
|
|
|
1. **Pre-execute checkpoint**: Created before the execute phase starts.
|
|
Captures the sandbox state so that a failed execution can be rolled back.
|
|
|
|
2. **Post-execute checkpoint**: Created after a successful execute phase.
|
|
Preserves the post-execution state before apply begins.
|
|
|
|
3. **Pre-apply checkpoint**: Created before the apply phase starts.
|
|
Allows rollback if the apply fails or encounters merge conflicts.
|
|
|
|
4. **Rollback on failure**: When execute or apply fails, the system
|
|
attempts to restore the sandbox to the most recent checkpoint.
|
|
|
|
### Integration with Plan Executor
|
|
|
|
The `PlanExecutor` accepts an optional `checkpoint_manager` parameter.
|
|
When provided, checkpoint hooks are automatically invoked:
|
|
|
|
- Before `run_execute`: `create_checkpoint(sandbox, plan_id, "pre_execute")`
|
|
- After successful execute: `create_checkpoint(sandbox, plan_id, "post_execute")`
|
|
- On execute failure: `rollback_to(last_checkpoint)`
|
|
|
|
When no `CheckpointManager` is injected, all hooks are silently skipped.
|
|
|
|
### Integration with Plan Apply Service
|
|
|
|
The `PlanApplyService` also accepts an optional `checkpoint_manager`:
|
|
|
|
- Before apply: `create_checkpoint(sandbox, plan_id, "pre_apply")`
|
|
- On apply failure: `rollback_to(last_checkpoint)`
|
|
|
|
### Thread Safety
|
|
|
|
The `CheckpointManager` is thread-safe. All mutable state is protected
|
|
by a reentrant lock (`threading.RLock`).
|
|
|
|
### Snapshot Storage
|
|
|
|
Snapshots are stored in temporary directories under the system temp folder.
|
|
Each snapshot is a full copy of the sandbox working directory at the time
|
|
of checkpoint creation. Snapshots are cleaned up when:
|
|
|
|
- A checkpoint is explicitly deleted via `delete_checkpoint()`
|
|
- The checkpoint manager goes out of scope (manual cleanup recommended)
|