Compare commits

...

1 Commits

Author SHA1 Message Date
HAL9000 7a0b884359 docs: add CLI reference for decisions, invariants, checkpoints, and plan corrections (v3.2.0/v3.3.0) [AUTO-DOCS-1]
CI / lint (pull_request) Successful in 27s
CI / quality (pull_request) Successful in 42s
CI / typecheck (pull_request) Successful in 54s
CI / security (pull_request) Successful in 1m29s
CI / helm (pull_request) Successful in 36s
CI / build (pull_request) Successful in 44s
CI / push-validation (pull_request) Successful in 27s
CI / integration_tests (pull_request) Successful in 4m23s
CI / e2e_tests (pull_request) Successful in 4m24s
CI / unit_tests (pull_request) Successful in 6m10s
CI / docker (pull_request) Successful in 1m39s
CI / coverage (pull_request) Successful in 11m37s
CI / status-check (pull_request) Successful in 1s
2026-04-14 02:37:56 +00:00
6 changed files with 1244 additions and 0 deletions
+28
View File
@@ -5,6 +5,34 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Documentation
- **CLI Reference: Decisions (v3.2.0)**: Added `docs/api/decisions.md` documenting
`agents plan tree` and `agents plan explain` commands, decision recording during
the Strategize phase, the full decision data model (id, plan_id, phase,
context_snapshot, alternatives), and the `DecisionService` Python API.
- **CLI Reference: Invariants (v3.2.0)**: Added `docs/api/invariants.md` documenting
`agents invariant add`, `agents invariant list`, and `agents invariant remove`
commands, the invariant scope hierarchy (global/project/action/plan), merge
precedence rules, enforcement records, violation model, and the `InvariantService`
Python API.
- **CLI Reference: Checkpoints (v3.3.0)**: Added `docs/api/checkpoints.md` documenting
`agents plan checkpoint list` and `agents plan rollback <checkpoint_id>` commands,
automatic checkpoint triggers (`on_tool_write`, `on_tool_write_complete`,
`on_subplan_spawn`, `on_error`), the checkpoint data model, retention policy, and
the `CheckpointService` Python API.
- **CLI Reference: Plan Corrections & Subplans (v3.3.0)**: Added
`docs/api/plan-corrections.md` documenting `agents plan correct --mode=revert`
and `agents plan correct --mode=append` commands, the correction data model,
`CorrectionAttemptRecord` lifecycle, and a subplan system overview covering
execution modes, merge strategies, and the `SubplanService` Python API.
- **API Index Update**: Extended `docs/api/index.md` with a new "Plan Intelligence
(v3.2.0 / v3.3.0)" section linking to all four new API reference pages.
### Fixed
- **Automation Profile Silent Fallback** (#8232): `_resolve_profile_for_plan` in
+276
View File
@@ -0,0 +1,276 @@
# Checkpoints API Reference
Checkpoints allow operators to snapshot sandbox state during plan execution and
restore it later. They are used for recovering from mistakes, reverting tool
side-effects, and supporting the decision-correction revert flow.
> **Version:** Introduced in v3.3.0. Automatic checkpoint triggers added in v3.8.0.
> **See also:** [`docs/reference/checkpointing.md`](../reference/checkpointing.md)
---
## Concept and Purpose
A **Checkpoint** is an immutable record of sandbox state at a specific point in
time. The execution engine creates checkpoints automatically at key moments
(before and after write-tool executions, before subplan spawning, and on
errors). Operators can also roll back to any checkpoint via the CLI.
### Automatic Checkpoint Triggers (v3.8.0+)
| Trigger | When |
|--------------------------|---------------------------------------------------|
| `on_tool_write` | Before each write-tool execution |
| `on_tool_write_complete` | After each write-tool execution |
| `on_subplan_spawn` | Before first subplan execution attempt |
| `on_error` | When the Execute phase fails |
All four triggers are enabled by default. See
[Configuration](#configuration) to disable specific triggers.
---
## CLI Commands
### `agents plan checkpoint list`
Lists all checkpoints available for a plan.
#### Synopsis
```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 (rich table)
agents plan checkpoint list 01HXYZ1234567890ABCDEFGH
# JSON output for scripting
agents plan checkpoint list 01HXYZ1234567890ABCDEFGH --format json
```
#### Output (rich)
The rich renderer displays a table with columns:
| Column | Description |
|-----------------|-------------------------------------------------------|
| ID | Truncated checkpoint ULID |
| Type | `pre_write`, `post_step`, or `manual` |
| Decision | Aligned decision ULID (if set) |
| Size | Checkpoint data size in bytes |
| Created | Creation timestamp (UTC) |
| Phase | Plan phase when checkpoint was created |
---
### `agents plan rollback`
Rolls back sandbox state to a previously captured checkpoint.
#### Synopsis
```bash
agents plan rollback [--yes|-y] <PLAN_ID> <CHECKPOINT_ID> [OPTIONS]
```
#### Arguments
| Argument | Description |
|------------------|-------------------------------------------------------|
| `PLAN_ID` | ULID of the plan to roll back |
| `CHECKPOINT_ID` | ULID of the checkpoint to restore |
#### Options
| Flag | Description |
|-------------------|-------------------------------------------------------|
| `--yes`, `-y` | Skip the interactive confirmation prompt |
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich`|
#### Examples
```bash
# Interactive rollback (prompts for confirmation)
agents plan rollback 01HPLAN... 01HCHKPT...
# Skip confirmation (useful in scripts)
agents plan rollback --yes 01HPLAN... 01HCHKPT...
# JSON output
agents plan rollback --yes 01HPLAN... 01HCHKPT... --format json
```
#### JSON Output Envelope
```json
{
"rollback_summary": {
"plan_id": "01HPLAN...",
"from_checkpoint_id": "01HCHKPT...",
"restored_files_count": 3
},
"changes_reverted": [
"path/to/file1.py",
"path/to/file2.py",
"path/to/file3.py"
],
"impact": {
"files_affected": 3
},
"post_rollback_state": {
"active_checkpoint": "01HCHKPT...",
"plan_id": "01HPLAN..."
},
"timing": {
"elapsed_seconds": 0.042
},
"messages": ["Rollback completed successfully."]
}
```
#### Error Cases
| Error | Cause |
|----------------------------------------------|----------------------------------------------------|
| `Rollback blocked: plan is already applied` | Plan has reached the 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 |
---
## Configuration
Automatic checkpoint triggers are configured via `core.checkpoints.auto_create_on`:
```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:
```toml
[core.checkpoints]
# Disable post-write checkpoints to reduce storage usage
auto_create_on = ["on_tool_write", "on_subplan_spawn", "on_error"]
```
To disable all automatic checkpoints:
```toml
[core.checkpoints]
auto_create_on = []
```
---
## Checkpoint Data Model
**Module:** `cleveragents.domain.models.core.checkpoint`
| Field | Type | Description |
|--------------------|-----------------|-----------------------------------------------------|
| `checkpoint_id` | `str` (ULID) | Unique identifier |
| `plan_id` | `str` (ULID) | The plan that owns this checkpoint |
| `sandbox_ref` | `str` | Git commit hash or patch reference |
| `decision_id` | `str \| None` | Optional decision ULID this checkpoint is aligned to|
| `checkpoint_type` | `CheckpointType`| `pre_write`, `post_step`, or `manual` |
| `resource_id` | `str \| None` | Optional resource ULID 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` | `CheckpointMetadata` | Audit metadata (reason, source tool, phase) |
### `CheckpointMetadata`
| Field | Type | Description |
|---------------|--------------|-----------------------------------------------------|
| `reason` | `str` | Why the checkpoint was created |
| `source_tool` | `str \| None`| Which tool triggered the checkpoint |
| `phase` | `str \| None`| Which plan phase was active |
| `extra` | `dict` | Arbitrary key-value pairs for extension |
### Retention Policy
A `CheckpointRetentionPolicy` governs how many checkpoints a plan may keep:
| Field | Default | Range |
|-------------------|---------|---------|
| `max_checkpoints` | 50 | 1100 |
| `auto_prune` | `true` | |
When `auto_prune` is enabled and the checkpoint count exceeds `max_checkpoints`,
the oldest **interior** checkpoints are automatically removed. The first
(earliest) and most recent checkpoints are always preserved.
---
## Python API
### `CheckpointService`
**Module:** `cleveragents.application.services.checkpoint_service`
```python
from cleveragents.application.services.checkpoint_service import CheckpointService
from cleveragents.domain.models.core.checkpoint import CheckpointType
svc = CheckpointService(repository=repo, settings=settings)
# Create a manual checkpoint
checkpoint = svc.create_checkpoint(
plan_id="01HPLAN...",
sandbox_ref="abc123def456",
checkpoint_type=CheckpointType.MANUAL,
decision_id=None,
metadata={"reason": "pre-migration snapshot", "phase": "execute"},
)
# List checkpoints for a plan
checkpoints = svc.list_checkpoints(plan_id="01HPLAN...")
# Get a specific checkpoint
cp = svc.get_checkpoint(checkpoint_id="01HCHKPT...")
# Roll back to a checkpoint
result = svc.rollback_to_checkpoint(
plan_id="01HPLAN...",
checkpoint_id="01HCHKPT...",
)
# result.restored_files_count, result.changed_paths, result.from_checkpoint_id
# Delete a checkpoint
svc.delete_checkpoint(checkpoint_id="01HCHKPT...")
```
### Dependency Injection
The `CheckpointService` is registered in the DI container and can be obtained
via:
```python
from cleveragents.di import get_container
container = get_container()
checkpoint_service = container.checkpoint_service()
```
---
## Related Documentation
- [Checkpointing and Rollback Reference](../reference/checkpointing.md) — full reference
- [Decision Correction Reference](../reference/decision_correction.md) — correction-service integration
- [Plan CLI Reference](../reference/plan_cli.md) — plan commands overview
- [Subplan Service](../reference/subplan_service.md) — `on_subplan_spawn` trigger
+259
View File
@@ -0,0 +1,259 @@
# Decisions API Reference
The decisions subsystem records every choice point in a plan's lifecycle as a
**Decision** node in a persistent tree. Decisions are created during the
Strategize and Execute phases and form the basis for targeted correction and
replay.
> **Version:** Introduced in v3.2.0.
> **See also:** [`docs/reference/decision_model.md`](../reference/decision_model.md),
> [`docs/reference/decision_service.md`](../reference/decision_service.md),
> [`docs/reference/decision_correction.md`](../reference/decision_correction.md)
---
## CLI Commands
### `agents plan tree`
Renders the full decision tree for a plan, showing every recorded choice point
and how they relate to one another.
#### Synopsis
```bash
agents plan tree <PLAN_ID> [OPTIONS]
```
#### Options
| Flag | Description |
|----------------------|---------------------------------------------------------------|
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
| `--show-superseded` | Include superseded (corrected) decisions in the tree |
| `--depth` | Maximum tree depth to display (`0` = unlimited, default: `0`) |
#### Examples
```bash
# Default rich tree view
agents plan tree 01HXYZ1234567890ABCDEFGH
# Table format
agents plan tree 01HXYZ1234567890ABCDEFGH --format table
# Include superseded decisions (useful when reviewing corrections)
agents plan tree 01HXYZ1234567890ABCDEFGH --show-superseded
# Limit depth to 2 levels
agents plan tree 01HXYZ1234567890ABCDEFGH --depth 2
# JSON output for scripting
agents plan tree 01HXYZ1234567890ABCDEFGH --format json
```
#### Output (rich)
The rich renderer displays a tree rooted at the `prompt_definition` decision.
Each node shows:
- Decision ID (truncated ULID)
- Decision type (e.g., `strategy_choice`, `implementation_choice`)
- Chosen option (truncated)
- Confidence score (if set)
- Superseded indicator (`[superseded]`) when `--show-superseded` is active
---
### `agents plan explain`
Shows full details for a single decision, including the question that was
answered, the chosen option, all alternatives considered, and optional context
snapshot and actor reasoning.
#### Synopsis
```bash
agents plan explain <DECISION_ID> [OPTIONS]
```
#### Options
| Flag | Description |
|----------------------|---------------------------------------------------------------|
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
| `--show-context` | Include context snapshot details (hash, resource refs) |
| `--show-reasoning` | Include rationale and raw actor reasoning trace |
Alternatives considered are always included in the output.
#### Examples
```bash
# Default rich output
agents plan explain 01HXYZ1234567890ABCDEFGH
# JSON with full context snapshot
agents plan explain 01HXYZ1234567890ABCDEFGH --format json --show-context
# Show actor reasoning trace
agents plan explain 01HXYZ1234567890ABCDEFGH --show-reasoning
# YAML output with all details
agents plan explain 01HXYZ1234567890ABCDEFGH --format yaml \
--show-context --show-reasoning
```
---
## Decision Recording During Strategize
During the **Strategize** phase, the strategy actor records decisions for every
significant choice it makes. The decision tree is built incrementally:
1. A `prompt_definition` root decision is created when the plan is initialised.
2. An `invariant_enforced` decision is recorded for each invariant applied by
the Invariant Reconciliation Actor.
3. One or more `strategy_choice` decisions capture the high-level approach.
4. `subplan_spawn` or `subplan_parallel_spawn` decisions are recorded when the
strategy decomposes work into child plans.
During the **Execute** phase, `implementation_choice`, `resource_selection`,
`tool_invocation`, `error_recovery`, and `validation_response` decisions are
added as children of the relevant strategy decisions.
---
## Decision Data Model
**Module:** `cleveragents.domain.models.core.decision`
### Identity Fields
| Field | Type | Description |
|------------------------|-----------------|-----------------------------------------------------|
| `decision_id` | `str` (ULID) | Auto-generated unique identifier |
| `plan_id` | `str` (ULID) | Parent plan (required) |
| `parent_decision_id` | `str \| None` | Parent decision; `None` for the root |
| `sequence_number` | `int` | Monotonic order within the plan (0-indexed) |
### Classification
| Field | Type | Description |
|-----------------|----------------|------------------------------------------------------|
| `decision_type` | `DecisionType` | One of 11 enum values (see table below) |
#### `DecisionType` Values
| Value | Phase | Description |
|--------------------------|------------|-------------------------------------------|
| `prompt_definition` | Strategize | Root decision — the plan prompt |
| `invariant_enforced` | Strategize | An invariant constraint was applied |
| `strategy_choice` | Strategize | High-level approach chosen |
| `implementation_choice` | Execute | How to implement a specific task |
| `resource_selection` | Execute | Which resources to read / modify |
| `subplan_spawn` | Strategize | Decision to create a child plan |
| `subplan_parallel_spawn` | Strategize | Spawn a group of child plans in parallel |
| `tool_invocation` | Execute | Which skill / tool to use |
| `error_recovery` | Execute | How to handle a failure |
| `validation_response` | Execute | Response to a validation failure |
| `user_intervention` | Any | User-provided guidance / correction |
### Content Fields
| Field | Type | Description |
|--------------------------|-----------------|-----------------------------------------------------|
| `question` | `str` | What question was being answered (required) |
| `chosen_option` | `str` | The option that was selected (required) |
| `alternatives_considered`| `list[str]` | Other options evaluated |
| `confidence_score` | `float \| None` | 0.01.0 confidence, or `None` |
### Context Snapshot
Every decision captures a `ContextSnapshot` for replay:
| Field | Type | Description |
|---------------------|--------------|--------------------------------------------------|
| `hot_context_hash` | `str` | SHA-256 hash of the context window |
| `hot_context_ref` | `str` | Storage pointer to the full serialised context |
| `relevant_resources`| `list[ResourceRef]` | Resources referenced at decision time |
| `actor_state_ref` | `str \| None`| LangGraph actor checkpoint reference |
### Rationale Fields
| Field | Type | Description |
|------------------|--------------|-----------------------------------------------------|
| `rationale` | `str` | Human-readable explanation |
| `actor_reasoning`| `str \| None`| Raw LLM reasoning trace, if available |
### Correction Metadata
| Field | Type | Description |
|------------------------|-----------------|-------------------------------------------------|
| `is_correction` | `bool` | `True` if this decision corrects another |
| `corrects_decision_id` | `str \| None` | ULID of the original decision |
| `correction_reason` | `str \| None` | Why the correction was made |
| `superseded_by` | `str \| None` | ULID of the decision that replaced this one |
---
## Python API
### `DecisionService`
**Module:** `cleveragents.application.services.decision_service`
```python
from cleveragents.application.services.decision_service import DecisionService
from cleveragents.domain.models.core.decision import DecisionType
svc = DecisionService(settings=settings, unit_of_work=uow)
# Record a decision during Strategize
decision = svc.record_decision(
plan_id="01HV...",
decision_type=DecisionType.STRATEGY_CHOICE,
question="Which approach should we use?",
chosen_option="Build a REST API with FastAPI",
alternatives_considered=["GraphQL", "gRPC"],
confidence_score=0.85,
rationale="REST is simpler and better supported by existing tooling.",
)
# Retrieve the full decision tree (BFS, level by level)
tree = svc.get_tree(plan_id="01HV...")
# Get a single decision
d = svc.get_decision(decision_id="01HXYZ...")
# List all decisions for a plan (ordered by sequence number)
decisions = svc.list_decisions(plan_id="01HV...")
# List decisions of a specific type
strategy_decisions = svc.list_by_type(
plan_id="01HV...",
decision_type=DecisionType.STRATEGY_CHOICE,
)
# Walk from a decision up to the root
path = svc.get_path_to_root(decision_id="01HXYZ...")
```
### Exceptions
| Exception | When raised |
|-------------------------|--------------------------------------------------|
| `DecisionNotFoundError` | Decision ID does not exist |
| `DuplicateDecisionError`| Decision ID already exists |
| `SequenceConflictError` | Sequence number already used for this plan |
| `ValidationError` | Required fields missing or empty |
---
## Related Documentation
- [Decision Domain Model](../reference/decision_model.md) — full field reference
- [Decision Service Reference](../reference/decision_service.md) — service API details
- [Decision Correction Reference](../reference/decision_correction.md) — correction modes
- [Plan CLI Reference](../reference/plan_cli.md) — `agents plan tree` / `agents plan explain`
- [Subplan Service](../reference/subplan_service.md) — spawn decisions
+13
View File
@@ -19,6 +19,19 @@ classes, functions, and exceptions with signatures and usage examples.
| [`cleveragents.providers`](providers.md) | AI provider registry — discovery, selection, LLM factory, and capability metadata |
| [`cleveragents.tui`](tui.md) | Interactive Terminal UI — app, persona system, input routing, slash commands, session export/import |
## Plan Intelligence (v3.2.0 / v3.3.0)
The following pages document the CLI commands and Python APIs introduced in
v3.2.0 (Decisions & Invariants) and v3.3.0 (Corrections, Subplans &
Checkpoints).
| Page | Description |
|------|-------------|
| [Decisions](decisions.md) | `agents plan tree`, `agents plan explain`, decision recording during Strategize, decision data model and Python API |
| [Invariants](invariants.md) | `agents invariant add/list/remove`, invariant scopes, enforcement, data model and Python API |
| [Checkpoints](checkpoints.md) | `agents plan checkpoint list`, `agents plan rollback`, automatic triggers, data model and Python API |
| [Plan Corrections](plan-corrections.md) | `agents plan correct --mode=revert\|append`, correction data model, subplan system overview and Python API |
> **Note:** Internal modules (prefixed with `_`) and implementation details
> not listed in a module's `__all__` are considered private and may change
> without notice.
+314
View File
@@ -0,0 +1,314 @@
# Invariants API Reference
Invariants are natural-language constraints on plan execution. They flow into
the decision tree during the Strategize phase and are reconciled by the
Invariant Reconciliation Actor before any strategy work begins.
> **Version:** Introduced in v3.2.0.
> **See also:** [`docs/reference/invariants.md`](../reference/invariants.md)
---
## Concept and Purpose
An **invariant** is a constraint that must hold throughout plan execution.
Examples:
- `"Never delete production data"`
- `"All API changes need tests"`
- `"Use Python 3.13 only"`
Invariants are evaluated at the start of the Strategize phase. If any
invariant is violated during execution, an `INVARIANT_VIOLATED` event is
emitted and the phase transition is blocked.
### Scope Hierarchy
| Scope | Description |
|-----------|----------------------------------------------------------|
| `GLOBAL` | Applies to every plan in the system |
| `PROJECT` | Applies to plans targeting a specific project |
| `ACTION` | Defined in an action template; promoted on `plan use` |
| `PLAN` | Attached directly to a specific plan |
### Merge Precedence
When computing the effective invariant set for a plan:
```
plan > project > global
```
Action-level invariants are promoted to plan scope when `plan use` is called.
Invariants are de-duplicated by text (case-insensitive); when the same
constraint appears at multiple scopes, only the highest-precedence copy is kept.
---
## CLI Commands
### `agents invariant add`
Creates a new invariant at the specified scope.
#### Synopsis
```bash
agents invariant add [SCOPE_FLAG] <TEXT> [OPTIONS]
```
#### Scope Flags (mutually exclusive)
| Flag | Description |
|-------------------|-------------------------------------------------------|
| `--global` | Create a global invariant (applies to all plans) |
| `--project NAME` | Create a project-scoped invariant |
| `--plan PLAN_ID` | Create a plan-scoped invariant |
| `--action NAME` | Create an action-scoped invariant |
#### Options
| Flag | Description |
|-------------------|-------------------------------------------------------|
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich`|
#### Examples
```bash
# Global invariant (applies to every plan)
agents invariant add --global "Never delete production data"
# Project-scoped invariant
agents invariant add --project myapp "All API changes need tests"
# Plan-specific invariant
agents invariant add --plan 01HXYZ... "Use Python 3.13 only"
# Action-scoped invariant
agents invariant add --action local/code-coverage "Minimum 80% coverage"
# JSON output
agents invariant add --global "No hardcoded secrets" --format json
```
#### Output
On success, the command prints the created invariant's ID and text:
```
✓ OK Invariant 01HABC... created
Scope: global
Text: Never delete production data
```
---
### `agents invariant list`
Displays invariants, optionally filtered by scope or matched by regex.
#### Synopsis
```bash
agents invariant list [REGEX] [OPTIONS]
```
#### Options
| Flag | Description |
|-------------------------|---------------------------------------------------------------|
| `--global` | Show only global invariants |
| `--project NAME` | Show only invariants for the specified project |
| `--plan PLAN_ID` | Show only invariants for the specified plan |
| `--action NAME` | Show only invariants for the specified action |
| `--effective` | Show the merged effective set (requires `--project`) |
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
#### Examples
```bash
# List all active invariants
agents invariant list
# Filter by scope
agents invariant list --global
agents invariant list --project myapp
# Show merged effective set for a project
agents invariant list --effective --project myapp
# Filter by regex
agents invariant list "data.*safe"
# JSON output
agents invariant list --format json
```
#### Output (rich)
The rich renderer displays a table with columns:
| Column | Description |
|-------------|---------------------------------------------------|
| ID | Truncated invariant ULID |
| Scope | `global`, `project`, `action`, or `plan` |
| Scope Target| Project/plan/action name (if applicable) |
| Text | Invariant constraint text |
| Created | Creation timestamp |
---
### `agents invariant remove`
Removes an invariant by ID.
#### Synopsis
```bash
agents invariant remove [--yes|-y] <INVARIANT_ID> [OPTIONS]
```
#### Options
| Flag | Description |
|-------------------|-------------------------------------------------------|
| `--yes`, `-y` | Skip the interactive confirmation prompt |
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich`|
#### Examples
```bash
# With confirmation prompt
agents invariant remove 01HXYZ...
# Skip confirmation (useful in scripts)
agents invariant remove --yes 01HXYZ...
```
#### Notes
- Removing a `GLOBAL` or `PROJECT` invariant does not retroactively affect
plans that have already been strategized — the invariant was already
evaluated and recorded in the decision tree.
- Removing a `PLAN`-scoped invariant from a plan that is still in the
Strategize phase will affect the next reconciliation run.
---
## Invariant Data Model
**Module:** `cleveragents.domain.models.core.invariant`
| Field | Type | Description |
|---------------|---------------|-----------------------------------------------------|
| `id` | `str` (ULID) | Auto-generated unique identifier |
| `name` | `str` | Short human-readable label (optional) |
| `description` | `str` | The constraint text (required, non-empty) |
| `scope` | `InvariantScope` | `GLOBAL`, `PROJECT`, `ACTION`, or `PLAN` |
| `scope_target`| `str \| None` | Project name, plan ID, or action name (scope-dependent) |
| `created_at` | `datetime` | UTC creation timestamp |
| `is_active` | `bool` | Whether the invariant is currently enforced |
### `InvariantScope` Enum
```python
from cleveragents.domain.models.core.invariant import InvariantScope
InvariantScope.GLOBAL # "global"
InvariantScope.PROJECT # "project"
InvariantScope.ACTION # "action"
InvariantScope.PLAN # "plan"
```
### Enforcement Record
When the Invariant Reconciliation Actor evaluates an invariant, it creates an
`InvariantEnforcementRecord`:
| Field | Description |
|------------------|--------------------------------------------------|
| `invariant_id` | ULID of the invariant |
| `enforced` | Whether the invariant was successfully enforced |
| `actor_response` | Response text from the reconciliation actor |
| `decision_id` | ULID of the associated `invariant_enforced` decision |
### Violation Model
When an invariant is violated during execution, an `InvariantViolation` is
created:
| Field | Description |
|-----------------|---------------------------------------------------|
| `invariant_id` | ULID of the violated invariant |
| `violated_text` | The invariant text that was violated |
| `severity` | `error`, `warning`, or `info` |
| `details` | Additional violation context |
---
## Python API
### `InvariantService`
**Module:** `cleveragents.application.services.invariant_service`
```python
from cleveragents.application.services.invariant_service import InvariantService
from cleveragents.domain.models.core.invariant import InvariantScope
svc = InvariantService(unit_of_work=uow)
# Create a global invariant
inv = svc.create_invariant(
description="Never delete production data",
scope=InvariantScope.GLOBAL,
)
# Create a project-scoped invariant
inv = svc.create_invariant(
description="All API changes need tests",
scope=InvariantScope.PROJECT,
scope_target="myapp",
)
# List all invariants
all_invariants = svc.list_invariants()
# List by scope
global_invariants = svc.list_invariants(scope=InvariantScope.GLOBAL)
project_invariants = svc.list_invariants(
scope=InvariantScope.PROJECT,
scope_target="myapp",
)
# Get the merged effective set for a project
effective = svc.get_effective_invariants(project_name="myapp")
# Remove an invariant
svc.remove_invariant(invariant_id="01HXYZ...")
```
### `InvariantReconciliationActor`
The reconciliation actor runs automatically at every plan phase transition.
It is wired into the plan lifecycle and does not need to be invoked manually.
```python
# The actor is resolved from the DI container:
from cleveragents.di import get_container
container = get_container()
actor = container.invariant_reconciliation_actor()
# Manually trigger reconciliation (advanced use):
result = await actor.reconcile(plan=plan, context=ctx)
```
---
## Related Documentation
- [Invariants Reference](../reference/invariants.md) — scope hierarchy, merge precedence, enforcement
- [Decision Model](../reference/decision_model.md) — `invariant_enforced` decision type
- [Plan CLI Reference](../reference/plan_cli.md) — `--invariant` flag on `agents plan use`
- [Automation Profiles](../reference/automation_profiles.md) — profile-level invariant interaction
+354
View File
@@ -0,0 +1,354 @@
# Plan Corrections API Reference
The plan correction subsystem allows operators to modify a plan's decision tree
after execution by either **reverting** a subtree of decisions or **appending**
new decisions as a child plan. This enables targeted re-execution without
discarding the entire plan.
> **Version:** Introduced in v3.3.0. Correction attempts tracking added in v3.8.0.
> **See also:** [`docs/reference/decision_correction.md`](../reference/decision_correction.md),
> [`docs/reference/subplans.md`](../reference/subplans.md),
> [`docs/reference/subplan_service.md`](../reference/subplan_service.md)
---
## Concept
### Correction Modes
#### Revert Mode
Invalidates the targeted decision and every descendant reachable via BFS
traversal. Associated artifacts are archived and affected child plans are
rolled back.
```text
┌─── D1 (target) ◄── revert starts here
│ │
│ ┌──┴──┐
│ D2 D3 ← all invalidated
│ │
│ D4 ← also invalidated
```
Use revert when the original decision was wrong and you want to re-execute
from that point with different guidance.
#### Append Mode
Preserves the original decision and spawns a **new child plan** rooted at the
target node. The child plan carries the operator's guidance and produces
additional decisions without disturbing the existing tree.
```text
D1 (target)
┌──┴──────────┐
D2 (original) CP-new ← child plan appended
```
Use append when the original decision was correct but you want to add
supplementary work without recomputing.
---
## CLI Commands
### `agents plan correct --mode=revert`
Re-executes from a targeted decision point by invalidating the decision subtree
and re-running from that node with new guidance.
#### Synopsis
```bash
agents plan correct --mode=revert <PLAN_ID> <DECISION_ID> [OPTIONS]
```
#### Arguments
| Argument | Description |
|---------------|-------------------------------------------------------|
| `PLAN_ID` | ULID of the plan to correct |
| `DECISION_ID` | ULID of the decision to revert from |
#### Options
| Flag | Description |
|-----------------------|-------------------------------------------------------|
| `--mode` | Correction mode: `revert` or `append` (required) |
| `--guidance TEXT` | Operator guidance for the re-execution (required) |
| `--dry-run` | Preview impact without making changes |
| `--yes`, `-y` | Skip the interactive confirmation prompt |
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich`|
#### Examples
```bash
# Revert a decision and re-execute with guidance
agents plan correct --mode=revert \
--guidance "Use a safer migration approach with explicit rollback" \
01HPLAN... 01HDECISION...
# Preview impact before committing
agents plan correct --mode=revert --dry-run \
--guidance "Use a safer migration approach" \
01HPLAN... 01HDECISION...
# Skip confirmation prompt
agents plan correct --mode=revert --yes \
--guidance "Use a safer migration approach" \
01HPLAN... 01HDECISION...
```
#### Dry-Run Output
A dry-run report includes:
- **impact** — Full `CorrectionImpact` with affected decisions, files, child
plans, estimated cost, and risk level
- **decisions_to_invalidate** — Decision IDs that *would* be marked invalid
- **child_plans_to_rollback** — Child plans that *would* be rolled back
- **estimated_recompute_time_seconds** — Wall-clock estimate
- **warnings** — Human-readable cautions (e.g., high risk)
#### Risk Classification
| Affected Decisions | Risk Level |
|--------------------|------------|
| ≤ 3 | low |
| 4 10 | medium |
| > 10 | high |
---
### `agents plan correct --mode=append`
Adds guidance to a plan by spawning a new child plan rooted at the target
decision, without recomputing the existing decision tree.
#### Synopsis
```bash
agents plan correct --mode=append <PLAN_ID> <DECISION_ID> [OPTIONS]
```
#### Arguments
| Argument | Description |
|---------------|-------------------------------------------------------|
| `PLAN_ID` | ULID of the plan to correct |
| `DECISION_ID` | ULID of the decision to append from |
#### Options
| Flag | Description |
|-----------------------|-------------------------------------------------------|
| `--mode` | Correction mode: `revert` or `append` (required) |
| `--guidance TEXT` | Operator guidance for the child plan (required) |
| `--dry-run` | Preview impact without making changes |
| `--yes`, `-y` | Skip the interactive confirmation prompt |
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich`|
#### Examples
```bash
# Append a child plan with additional guidance
agents plan correct --mode=append \
--guidance "Also add integration tests for the new endpoint" \
01HPLAN... 01HDECISION...
# Preview what would be created
agents plan correct --mode=append --dry-run \
--guidance "Also add integration tests" \
01HPLAN... 01HDECISION...
```
---
## Correction Status Lifecycle
```text
PENDING → ANALYZING → EXECUTING → APPLIED
→ FAILED
PENDING → CANCELLED
ANALYZING → CANCELLED
```
---
## Correction Data Model
**Module:** `cleveragents.domain.models.core.correction`
| Field | Type | Description |
|------------------------|-------------------|-------------------------------------------------|
| `correction_id` | `str` (ULID) | Unique identifier |
| `plan_id` | `str` (ULID) | Plan being corrected |
| `target_decision_id` | `str` (ULID) | Decision being corrected |
| `mode` | `CorrectionMode` | `revert` or `append` |
| `guidance` | `str` | Operator guidance text (110,000 chars) |
| `status` | `CorrectionStatus`| Current status (see lifecycle above) |
| `created_at` | `datetime` | UTC creation timestamp |
| `completed_at` | `datetime \| None`| UTC completion timestamp (terminal states only) |
### `CorrectionAttemptRecord` (v3.8.0+)
Each execution of a correction is tracked as a `CorrectionAttemptRecord`.
Multiple attempts may exist for a single correction (e.g., if a first attempt
fails and the operator retries).
| Field | Type | Description |
|--------------------------|-------------------------|-------------------------------------------------|
| `id` | `str` (ULID) | Unique identifier for this attempt |
| `correction_id` | `str` (ULID) | Parent correction ULID |
| `plan_id` | `str` (ULID) | Plan this correction targets |
| `original_decision_id` | `str` (ULID) | Decision being corrected |
| `new_decision_id` | `str \| None` | Replacement decision (set on success) |
| `mode` | `CorrectionMode` | `revert` or `append` |
| `state` | `CorrectionAttemptState`| `pending`, `executing`, `complete`, or `failed` |
| `guidance` | `str` | Operator guidance text |
| `created_at` | `datetime` | UTC creation timestamp |
| `completed_at` | `datetime \| None` | UTC completion timestamp |
| `archived_artifacts_path`| `str \| None` | Path to archived artifacts (revert mode) |
---
## Subplan System Overview
Subplans allow a parent plan to decompose work into coordinated child plans.
Each child plan runs in its own sandbox, and results are merged back into the
parent plan using a configurable merge strategy.
### How Subplans Are Spawned
During the **Strategize** phase, the strategy actor may decide to decompose
work by recording `subplan_spawn` or `subplan_parallel_spawn` decisions. The
`SubplanService` then:
1. Extracts spawn decisions from the `DecisionService`
2. Builds `SpawnEntry` objects from those decisions
3. Validates the spawn request (resource scopes, merge strategy, parallelism)
4. Creates `SubplanStatus` and `SpawnMetadata` for each entry
5. Returns the result for the caller to persist on the parent plan
### Execution Modes
| Mode | Description |
|-----------------------|--------------------------------------------------|
| `sequential` | Execute one at a time in order |
| `parallel` | Execute concurrently (up to `max_parallel`) |
| `dependency_ordered` | Respect DAG dependencies via topological sort |
### Merge Strategies
After subplans complete, their sandbox outputs are merged:
| Strategy | Description |
|-----------------------|--------------------------------------------------|
| `git_three_way` | Three-way merge via `git merge-file` |
| `sequential_apply` | Apply changes in completion order |
| `fail_on_conflict` | Raise `MergeConflictError` on any conflict |
| `last_wins` | Final subplan's output overwrites earlier ones |
### Subplan Configuration
```yaml
subplan_config:
execution_mode: parallel # sequential | parallel | dependency_ordered
merge_strategy: git_three_way # git_three_way | sequential_apply | fail_on_conflict | last_wins
max_parallel: 5 # 1-50, for parallel mode
fail_fast: false # stop all on first failure
timeout_per_subplan_seconds: ~ # optional per-subplan timeout
retry_failed: true # auto-retry failed subplans
max_retries: 2 # 0-5, max retry attempts
```
### Checkpoint Integration
Before the first subplan execution attempt, the `SubplanExecutionService`
automatically creates a checkpoint (the `on_subplan_spawn` trigger). This
allows rolling back to the pre-spawn state if subplan execution fails
catastrophically.
---
## Python API
### `CorrectionService`
**Module:** `cleveragents.application.services.correction_service`
```python
from cleveragents.application.services.correction_service import CorrectionService
from cleveragents.domain.models.core.correction import CorrectionMode
svc = CorrectionService(
decision_service=decision_svc,
plan_service=plan_svc,
checkpoint_service=checkpoint_svc, # optional
)
# Create a correction request
correction = svc.request_correction(
plan_id="01HPLAN...",
target_decision_id="01HDECISION...",
mode=CorrectionMode.REVERT,
guidance="Use a safer migration approach with explicit rollback",
)
# Analyze impact (BFS over decision tree)
impact = svc.analyze_impact(correction_id=correction.correction_id)
# impact.affected_decisions, impact.affected_files, impact.risk_level
# Generate a dry-run report (no side effects)
report = svc.generate_dry_run_report(correction_id=correction.correction_id)
# Execute the correction
result = svc.execute_correction(correction_id=correction.correction_id)
# List corrections for a plan
corrections = svc.list_corrections(plan_id="01HPLAN...")
# Cancel a pending correction
svc.cancel_correction(correction_id=correction.correction_id)
```
### `SubplanService`
**Module:** `cleveragents.application.services.subplan_service`
```python
from cleveragents.application.services.subplan_service import SubplanService
svc = SubplanService(
decision_service=decision_svc,
config=plan.subplan_config,
)
# Get spawn decisions for the parent plan
decisions = svc.get_spawn_decisions(parent_plan_id="01HPLAN...")
# Build spawn entries from decisions
entries = svc.build_spawn_entries(decisions)
# Spawn child plans (validates first)
result = svc.spawn(
parent_plan=parent_plan,
config=parent_plan.subplan_config,
spawn_entries=entries,
available_resources=frozenset(known_resource_ids),
)
# result.spawned_statuses, result.metadata, result.total_spawned
```
---
## Related Documentation
- [Decision Correction Reference](../reference/decision_correction.md) — full correction reference
- [Subplans: Execution and Merge](../reference/subplans.md) — subplan execution modes and merge strategies
- [Subplan Service Reference](../reference/subplan_service.md) — spawn workflow details
- [Checkpoints API Reference](checkpoints.md) — checkpoint and rollback
- [Decisions API Reference](decisions.md) — decision tree and recording
- [Plan CLI Reference](../reference/plan_cli.md) — plan commands overview