238 lines
6.6 KiB
Markdown
238 lines
6.6 KiB
Markdown
# Checkpoint and Rollback API (v3.3.0)
|
||
|
||
CleverAgents supports checkpointing and rollback to snapshot sandbox state during plan
|
||
execution and restore it later. This page documents the CLI commands and Python API for
|
||
managing checkpoints.
|
||
|
||
---
|
||
|
||
## Overview
|
||
|
||
Checkpointing introduced in **v3.3.0** allows operators to:
|
||
|
||
- Snapshot sandbox state at key points during plan execution.
|
||
- Roll back to a previous snapshot to undo tool side-effects.
|
||
- Integrate with the decision-correction revert flow for targeted re-execution.
|
||
|
||
Checkpoints are immutable records stored in the `checkpoint_metadata` SQLite table and
|
||
backed by a `CheckpointRepository`. The `CheckpointService` is registered in the DI
|
||
container and injected into `ToolRunner`, `SubplanExecutionService`, and `PlanExecutor`.
|
||
|
||
---
|
||
|
||
## CLI Reference
|
||
|
||
### `agents plan checkpoint list`
|
||
|
||
List all checkpoints for a plan.
|
||
|
||
```bash
|
||
agents plan checkpoint list <PLAN_ID> [OPTIONS]
|
||
```
|
||
|
||
**Options:**
|
||
|
||
| Flag | Description |
|
||
|------|-------------|
|
||
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
|
||
|
||
**Examples:**
|
||
|
||
```bash
|
||
# List checkpoints for a plan
|
||
agents plan checkpoint list 01HXYZ1234567890ABCDEFGH
|
||
|
||
# JSON output for scripting
|
||
agents plan checkpoint list 01HXYZ1234567890ABCDEFGH --format json
|
||
```
|
||
|
||
Each checkpoint entry shows:
|
||
|
||
| Field | Description |
|
||
|-------|-------------|
|
||
| `checkpoint_id` | ULID identifier |
|
||
| `checkpoint_type` | `pre_write`, `post_step`, or `manual` |
|
||
| `decision_id` | Optional decision this checkpoint is aligned to |
|
||
| `sandbox_ref` | Git commit hash or patch reference |
|
||
| `size_bytes` | Size of the checkpoint data |
|
||
| `created_at` | UTC creation timestamp |
|
||
|
||
---
|
||
|
||
### `agents plan rollback`
|
||
|
||
Roll back a plan's sandbox to a previously captured checkpoint.
|
||
|
||
```bash
|
||
agents plan rollback [--yes|-y] <PLAN_ID> <CHECKPOINT_ID>
|
||
```
|
||
|
||
**Options:**
|
||
|
||
| Flag | Description |
|
||
|------|-------------|
|
||
| `--yes`, `-y` | Skip the interactive confirmation prompt |
|
||
|
||
**Examples:**
|
||
|
||
```bash
|
||
# Rollback with confirmation prompt
|
||
agents plan rollback 01HXYZ1234567890ABCDEFGH 01HABC1234567890ABCDEFGH
|
||
|
||
# Skip confirmation (for scripts/CI)
|
||
agents plan rollback --yes 01HXYZ1234567890ABCDEFGH 01HABC1234567890ABCDEFGH
|
||
```
|
||
|
||
**JSON output envelope** (when using `--format json`):
|
||
|
||
```json
|
||
{
|
||
"rollback_summary": {
|
||
"plan_id": "...",
|
||
"from_checkpoint_id": "...",
|
||
"restored_files_count": 3
|
||
},
|
||
"changes_reverted": ["path/to/file1.py", "path/to/file2.py"],
|
||
"impact": {
|
||
"files_affected": 3
|
||
},
|
||
"post_rollback_state": {
|
||
"active_checkpoint": "...",
|
||
"plan_id": "..."
|
||
},
|
||
"timing": {
|
||
"elapsed_seconds": 0.042
|
||
},
|
||
"messages": ["Rollback completed successfully."]
|
||
}
|
||
```
|
||
|
||
**Error cases:**
|
||
|
||
| Error | Cause |
|
||
|-------|-------|
|
||
| `Rollback blocked: plan is already applied` | Plan has reached terminal `applied` state |
|
||
| `Rollback blocked: sandbox is missing` | Sandbox was cleaned up before rollback |
|
||
| `Not found: checkpoint` | Checkpoint ID does not exist |
|
||
| `Validation Error` | Checkpoint does not belong to the specified plan |
|
||
|
||
---
|
||
|
||
## Checkpoint Lifecycle
|
||
|
||
### Checkpoint Types
|
||
|
||
| Type | When Created |
|
||
|------|-------------|
|
||
| `pre_write` | Automatically before each write-tool execution |
|
||
| `post_step` | Automatically after each write-tool execution |
|
||
| `manual` | Via `CheckpointService.create_checkpoint()` directly |
|
||
|
||
### Automatic Checkpoint Triggers (v3.8.0+)
|
||
|
||
The execution engine supports four automatic triggers, configurable via
|
||
`core.checkpoints.auto_create_on`:
|
||
|
||
| Trigger | When | Component |
|
||
|---------|------|-----------|
|
||
| `on_tool_write` | Before each write-tool execution | `ToolRunner` |
|
||
| `on_tool_write_complete` | After each write-tool execution | `ToolRunner` |
|
||
| `on_subplan_spawn` | Before first subplan execution attempt | `SubplanExecutionService` |
|
||
| `on_error` | When the Execute phase fails | `PlanExecutor` |
|
||
|
||
**Configuration:**
|
||
|
||
```toml
|
||
[core.checkpoints]
|
||
auto_create_on = ["on_tool_write", "on_tool_write_complete", "on_subplan_spawn", "on_error"]
|
||
```
|
||
|
||
To disable all automatic checkpoints:
|
||
|
||
```toml
|
||
[core.checkpoints]
|
||
auto_create_on = []
|
||
```
|
||
|
||
### Retention Policy
|
||
|
||
A `CheckpointRetentionPolicy` governs how many checkpoints a plan may keep:
|
||
|
||
| Field | Default | Range |
|
||
|-------|---------|-------|
|
||
| `max_checkpoints` | 50 | 1–100 |
|
||
| `auto_prune` | `true` | — |
|
||
|
||
When `auto_prune` is enabled and the count exceeds `max_checkpoints`, the oldest
|
||
**interior** checkpoints are removed. The first (earliest) and most recent checkpoints
|
||
are always preserved.
|
||
|
||
---
|
||
|
||
## Checkpoint Data Model
|
||
|
||
Each checkpoint stores:
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `checkpoint_id` | ULID | Unique identifier |
|
||
| `plan_id` | ULID | The plan that owns this checkpoint |
|
||
| `sandbox_ref` | str | Git commit hash or patch reference |
|
||
| `decision_id` | ULID \| None | Optional decision alignment |
|
||
| `checkpoint_type` | str | `pre_write`, `post_step`, or `manual` |
|
||
| `resource_id` | ULID \| None | Optional resource association |
|
||
| `filesystem_path` | str | Relative path within the checkpoint directory |
|
||
| `size_bytes` | int | Size of the checkpoint data in bytes |
|
||
| `created_at` | datetime | UTC creation timestamp |
|
||
| `metadata` | dict | Audit metadata: `reason`, `source_tool`, `phase`, `extra` |
|
||
|
||
---
|
||
|
||
## Python API
|
||
|
||
The `CheckpointService` is obtained from the DI container:
|
||
|
||
```python
|
||
from cleveragents.application.container import get_container
|
||
|
||
checkpoint_service = get_container().checkpoint_service()
|
||
|
||
# Create a manual checkpoint
|
||
checkpoint = checkpoint_service.create_checkpoint(
|
||
plan_id="01HV...",
|
||
sandbox_ref="abc123",
|
||
checkpoint_type="manual",
|
||
metadata={"reason": "pre-correction snapshot", "phase": "execute"},
|
||
)
|
||
|
||
# List checkpoints for a plan
|
||
checkpoints = checkpoint_service.list_checkpoints(plan_id="01HV...")
|
||
|
||
# Roll back to a checkpoint
|
||
result = checkpoint_service.rollback_to_checkpoint(
|
||
plan_id="01HV...",
|
||
checkpoint_id="01HABC...",
|
||
)
|
||
print(f"Restored {result.restored_files_count} files")
|
||
```
|
||
|
||
---
|
||
|
||
## Rollback Guards
|
||
|
||
The rollback operation enforces two guards:
|
||
|
||
1. **Plan is applied** — Once a plan reaches the `applied` terminal state, rollback is
|
||
rejected with a `BusinessRuleViolation`.
|
||
2. **Sandbox is missing** — If the sandbox has been cleaned up, rollback is rejected
|
||
because there is nothing to restore.
|
||
|
||
---
|
||
|
||
## See Also
|
||
|
||
- [`docs/reference/checkpointing.md`](../reference/checkpointing.md) — Full checkpointing domain model reference
|
||
- [`docs/api/plan-corrections.md`](plan-corrections.md) — Plan correction modes (revert uses checkpoints)
|
||
- [ADR-015: Sandbox & Checkpoint](../adr/ADR-015-sandbox-and-checkpoint.md)
|
||
- [ADR-035: Decision Tree Rollback & Replay](../adr/ADR-035-decision-tree-rollback-and-replay.md)
|