Files
cleveragents-core/docs/reference/checkpointing.md
T
freemo 1f64a274e3
ci.yml / docs: document ACMS real retrieval logic and automatic checkpoint triggers (push) Failing after 0s
ci.yml / docs: document ACMS real retrieval logic and automatic checkpoint triggers (pull_request) Failing after 0s
docs: document ACMS real retrieval logic and automatic checkpoint triggers
- docs/reference/context_strategies.md: update Built-in Strategies table
  to document real retrieval logic for all 6 strategies (SimpleKeyword,
  SemanticEmbedding, BreadthDepthNavigator, ARCE, TemporalArchaeology,
  PlanDecisionContext); add note about v1 character-frequency embedding
  approximation; note SpecStrategyAdapter registration (#3500)
- docs/reference/checkpointing.md: add Automatic Checkpoint Triggers
  section documenting the 4 triggers (on_tool_write, on_tool_write_complete,
  on_subplan_spawn, on_error), configuration via core.checkpoints.auto_create_on,
  and wiring into ToolRunner/SubplanExecutionService/PlanExecutor (#3439)
2026-04-05 21:31:44 +00:00

198 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Checkpointing and Rollback
## Overview
CleverAgents supports checkpointing and rollback to allow operators to
snapshot sandbox state during plan execution and restore it later. This
is useful for recovering from mistakes, reverting tool side-effects, and
supporting the decision-correction revert flow.
## Concepts
### Checkpoint
A **Checkpoint** is an immutable record of sandbox state at a point in
time. Each checkpoint stores:
- `checkpoint_id` — Unique ULID identifier
- `plan_id` — The plan that owns this checkpoint
- `sandbox_ref` — A reference to the sandbox state (e.g. git commit hash)
- `decision_id` — Optional decision ULID this checkpoint is aligned to
- `checkpoint_type` — One of `pre_write`, `post_step`, or `manual`
- `resource_id` — Optional resource ULID association
- `filesystem_path` — Relative path within the checkpoint directory
- `size_bytes` — Size of the checkpoint data in bytes
- `created_at` — Timestamp of creation (UTC)
- `metadata` — Audit metadata including reason, source tool, and phase
### Retention Policy
A **CheckpointRetentionPolicy** governs how many checkpoints a plan may
keep. When `auto_prune` is enabled and the number of checkpoints exceeds
`max_checkpoints`, the oldest **interior** checkpoints are automatically
removed when a new one is created. The first (earliest) and most recent
checkpoints are always preserved during pruning.
**Defaults:**
| Field | Default | Range |
|-------------------|---------|---------|
| `max_checkpoints` | 50 | 1100 |
| `auto_prune` | true | |
### Rollback
A **rollback** restores sandbox state to a previously captured checkpoint.
The `RollbackResult` describes what changed:
- `restored_files_count` — Number of files restored
- `changed_paths` — Paths that were modified during rollback
- `from_checkpoint_id` — The checkpoint that was restored
## 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.
## Automatic Checkpoint Triggers (v3.8.0+)
The execution engine now supports four automatic checkpoint triggers, all
configurable via `core.checkpoints.auto_create_on` (default: all enabled):
| 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 a specific trigger, remove it from the list. To disable all
automatic checkpoints:
```toml
[core.checkpoints]
auto_create_on = []
```
### Wiring
The `CheckpointService` is injected as an optional parameter into:
- **`ToolRunner`** — `checkpoint_service` and `auto_checkpoint_triggers` parameters
- **`SubplanExecutionService`** — `checkpoint_service`, `auto_checkpoint_triggers`, and `parent_plan_id` parameters
- **`PlanExecutor`** — uses `_is_auto_trigger_active()` helper to check trigger config
When `checkpoint_service` is `None`, all automatic checkpointing is skipped
(backward-compatible no-op).
## CLI Usage
### Create a checkpoint (programmatic)
Checkpoints are created automatically by the execution engine via the four
triggers above, or manually via `CheckpointService.create_checkpoint()`.
The `CheckpointScope` on each tool's capability declaration controls which
tools trigger `on_tool_write` / `on_tool_write_complete` checkpoints.
### Rollback to a checkpoint
```bash
agents plan rollback [--yes|-y] <plan_id> <checkpoint_id>
```
The `--yes` / `-y` flag skips the interactive confirmation prompt.
**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 |
## Correction Service Integration
The `CorrectionService` accepts an optional `checkpoint_service` parameter
(typed as `CheckpointService | None`). This is an **integration point**
for a future milestone: once wired, the correction revert flow will
delegate sandbox restoration to the checkpoint rollback mechanism,
enabling reuse of the same rollback logic for both explicit CLI rollback
and decision-correction reverts. The delegation is **not yet active** in
the current implementation; `execute_revert()` performs BFS impact
analysis and marks decisions as reverted but does not invoke
`checkpoint_service.rollback_to_checkpoint()`.
## Database Schema
The `checkpoint_metadata` table stores checkpoint metadata:
| Column | Type | Description |
|-------------------|-------------|-------------------------------------|
| `checkpoint_id` | STRING(26) PK | ULID primary key |
| `plan_id` | STRING(26) FK | References `v3_plans.plan_id` |
| `decision_id` | STRING(26) | Optional decision alignment |
| `checkpoint_type` | TEXT | `pre_write`, `post_step`, `manual` |
| `resource_id` | STRING(26) | Optional resource association |
| `sandbox_ref` | TEXT | Git commit hash or patch reference |
| `filesystem_path` | TEXT | Relative path within checkpoint dir |
| `size_bytes` | INTEGER | Size of checkpoint data in bytes |
| `created_at` | STRING(30) | ISO-8601 timestamp |
| `metadata_json` | TEXT | JSON: reason, source_tool, phase |
Indexes: `idx_checkpoints_plan`, `ix_checkpoint_metadata_created_at`.
## Dependency Injection
The `CheckpointService` is registered in the DI container
(`container.py`) and backed by a `CheckpointRepository` that uses the
session-factory pattern (ADR-007). The CLI `rollback` command obtains
the service from the container via `get_container().checkpoint_service()`.
The `UnitOfWorkContext` also exposes a `checkpoints` property for
cross-repository atomicity within a single transaction.
## Metadata for Auditability
Each checkpoint stores structured metadata to support audit trails:
- **reason** — Why the checkpoint was created (e.g. "pre-execution")
- **source_tool** — Which tool triggered the checkpoint (e.g. "lint-check")
- **phase** — Which plan phase was active (e.g. "execute")
- **extra** — Arbitrary key-value pairs for extension