349 lines
11 KiB
Markdown
349 lines
11 KiB
Markdown
# Decision Recording and Tree API (v3.2.0)
|
||
|
||
The decision subsystem records every choice point during a plan's Strategize and Execute
|
||
phases as a persistent tree of **Decision** nodes. This page documents the CLI commands
|
||
and Python API for inspecting and navigating that tree.
|
||
|
||
---
|
||
|
||
## Overview
|
||
|
||
During the **Strategize** phase, the strategy actor records each significant choice as a
|
||
`Decision` node. Decisions are linked parent-to-child to form a tree rooted at the
|
||
`prompt_definition` node (the original plan prompt). The tree is persisted to SQLite via
|
||
`DecisionRepository` and can be queried at any time — even after the plan has completed.
|
||
|
||
Key capabilities introduced in **v3.2.0**:
|
||
|
||
- Automatic decision recording for every strategy and execution choice.
|
||
- Context snapshots (SHA-256 hash + storage pointer) captured alongside each decision.
|
||
- Alternatives-considered tracking for every decision node.
|
||
- Full tree rendering via `agents plan tree`.
|
||
- Per-decision explanation via `agents plan explain`.
|
||
|
||
---
|
||
|
||
## CLI Reference
|
||
|
||
### `agents plan tree`
|
||
|
||
Display the decision tree for a plan.
|
||
|
||
```bash
|
||
agents plan tree <PLAN_ID> [OPTIONS]
|
||
```
|
||
|
||
**Options:**
|
||
|
||
| Flag | Description |
|
||
|------|-------------|
|
||
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` (default: `rich`) |
|
||
| `--show-superseded` | Include superseded decisions in the tree |
|
||
| `--depth` | Maximum tree depth to render (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 (e.g. after a correction)
|
||
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
|
||
```
|
||
|
||
**Rich output** renders an indented tree with decision type labels, sequence numbers,
|
||
and full 26-character ULIDs so that IDs can be copied directly into follow-up commands
|
||
such as `agents plan explain` or `agents plan correct`.
|
||
|
||
---
|
||
|
||
### `agents plan explain`
|
||
|
||
Show full details for a single decision node, including alternatives considered,
|
||
context snapshot, and actor reasoning.
|
||
|
||
```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, storage ref, resources) |
|
||
| `--show-reasoning` | Include rationale and raw actor reasoning trace |
|
||
|
||
Alternatives considered are **always** included in the output regardless of flags.
|
||
|
||
**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 with all details
|
||
agents plan explain 01HXYZ1234567890ABCDEFGH --format yaml \
|
||
--show-context --show-reasoning
|
||
```
|
||
|
||
---
|
||
|
||
## Decision Data Model
|
||
|
||
Each decision node stores the following fields.
|
||
|
||
### Identity
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `decision_id` | ULID | Auto-generated unique identifier |
|
||
| `plan_id` | ULID | Parent plan |
|
||
| `parent_decision_id` | ULID \| None | Parent node; `None` for the root |
|
||
| `sequence_number` | int | Monotonic order within the plan (0-indexed, never reused) |
|
||
|
||
### Classification
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `decision_type` | `DecisionType` | One of 11 enum values (see table below) |
|
||
|
||
**Decision types:**
|
||
|
||
| Type | 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
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `question` | str | What question was being answered |
|
||
| `chosen_option` | str | The option that was selected |
|
||
| `alternatives_considered` | list[str] | Other options that were evaluated |
|
||
| `confidence_score` | float \| None | 0.0–1.0 confidence, or `None` |
|
||
|
||
### Context Snapshot
|
||
|
||
Every decision captures a `ContextSnapshot` for replay and correction:
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `hot_context_hash` | str | SHA-256 hash of the context window at decision time |
|
||
| `hot_context_ref` | str | Storage pointer to the full serialised context |
|
||
| `relevant_resources` | list[ResourceRef] | Resources in scope at decision time |
|
||
| `actor_state_ref` | str | LangGraph actor checkpoint reference |
|
||
|
||
### Rationale
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `rationale` | str | Human-readable explanation |
|
||
| `actor_reasoning` | str \| None | Raw LLM reasoning trace |
|
||
|
||
### Downstream Impact
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `downstream_decision_ids` | list[ULID] | Decisions that depend on this one |
|
||
| `downstream_plan_ids` | list[ULID] | Child plans spawned from this decision |
|
||
| `artifacts_produced` | list[ArtifactRef] | Artifacts created by this decision |
|
||
|
||
### Correction Metadata
|
||
|
||
| Field | Type | Description |
|
||
|-------|------|-------------|
|
||
| `is_correction` | bool | `True` if this decision corrects another |
|
||
| `corrects_decision_id` | ULID \| None | Original decision being corrected |
|
||
| `correction_reason` | str \| None | Why the correction was made |
|
||
| `superseded_by` | ULID \| None | Decision that replaced this one |
|
||
|
||
---
|
||
|
||
## Tree Structure
|
||
|
||
Decisions form a tree via `parent_decision_id`:
|
||
|
||
```
|
||
prompt_definition (root, parent=None)
|
||
├── invariant_enforced
|
||
├── strategy_choice
|
||
│ ├── implementation_choice
|
||
│ │ ├── resource_selection
|
||
│ │ └── tool_invocation
|
||
│ └── subplan_spawn
|
||
└── strategy_choice
|
||
```
|
||
|
||
The `prompt_definition` type is always the root and must have
|
||
`parent_decision_id = None`. The **current tree** consists of all decisions where
|
||
`superseded_by IS NULL`. Superseded decisions are hidden by default in `agents plan tree`
|
||
but can be shown with `--show-superseded`.
|
||
|
||
---
|
||
|
||
## Decision Persistence
|
||
|
||
Decisions are persisted to SQLite via `DecisionRepository` in the
|
||
`cleveragents.infrastructure.database` package. The service layer
|
||
(`DecisionService`) supports two modes:
|
||
|
||
| Mode | UnitOfWork | Storage |
|
||
|------|------------|---------|
|
||
| In-memory | `None` | Internal dicts |
|
||
| Persisted | provided | SQLite via `DecisionRepository` + in-memory write-through cache |
|
||
|
||
When a `UnitOfWork` is wired, mutations are written to the database first, then the
|
||
in-memory cache is updated (write-through).
|
||
|
||
**Python API — recording a decision:**
|
||
|
||
```python
|
||
from cleveragents.application.services.decision_service import DecisionService
|
||
from cleveragents.domain.models.core.decision import DecisionType
|
||
|
||
svc = DecisionService(unit_of_work=uow)
|
||
|
||
decision = svc.record_decision(
|
||
plan_id="01HV...",
|
||
decision_type=DecisionType.STRATEGY_CHOICE,
|
||
question="Which approach should we take?",
|
||
chosen_option="Build a REST API",
|
||
alternatives_considered=["GraphQL API", "gRPC service"],
|
||
confidence_score=0.85,
|
||
rationale="REST is simpler and better supported by existing tooling.",
|
||
)
|
||
```
|
||
|
||
**Python API — retrieving the tree:**
|
||
|
||
```python
|
||
# BFS from root(s), level by level
|
||
tree = svc.get_tree(plan_id="01HV...")
|
||
|
||
# Walk from a decision up to the root
|
||
path = svc.get_path_to_root(decision_id="01HXYZ...")
|
||
|
||
# List all decisions for a plan, ordered by sequence number
|
||
decisions = svc.list_decisions(plan_id="01HV...")
|
||
```
|
||
|
||
---
|
||
|
||
## See Also
|
||
|
||
- [`docs/reference/decision_model.md`](../reference/decision_model.md) — Full domain model reference
|
||
- [`docs/reference/decision_service.md`](../reference/decision_service.md) — `DecisionService` API reference
|
||
- [`docs/api/plan-corrections.md`](plan-corrections.md) — Correction modes and subplan system
|
||
- [ADR-007: Decision Tree & Correction](../adr/ADR-007-decision-tree-and-correction.md)
|
||
- [ADR-033: Decision Recording Protocol](../adr/ADR-033-decision-recording-protocol.md)
|
||
- [ADR-034: Decision Tree Versioning & History](../adr/ADR-034-decision-tree-versioning-and-history.md)
|
||
diff --git a/docs/api/invariants.md b/docs/api/invariants.md
|
||
new file mode 100644
|
||
index 0000000..2222222
|
||
--- /dev/null
|
||
+++ b/docs/api/invariants.md
|
||
@@ -0,0 +1,274 @@
|
||
# Invariant Management API (v3.2.0)
|
||
|
||
Invariants are natural-language constraints that govern plan execution. They are
|
||
evaluated at the start of the Strategize phase by the **Invariant Reconciliation Actor**
|
||
and recorded in the decision tree as `invariant_enforced` nodes.
|
||
|
||
---
|
||
|
||
## Overview
|
||
|
||
Invariants introduced in **v3.2.0** provide a declarative way to constrain what a plan
|
||
may do. They are scoped (global, project, action, or plan), merged by precedence, and
|
||
de-duplicated before enforcement. Violations block the phase transition and emit
|
||
`INVARIANT_VIOLATED` events.
|
||
|
||
Key capabilities:
|
||
|
||
- Four scope levels: `GLOBAL`, `PROJECT`, `ACTION`, `PLAN`.
|
||
- Merge precedence: plan > project > global (action invariants are promoted to plan scope).
|
||
- Automatic enforcement at every phase transition via `InvariantReconciliationActor`.
|
||
- CLI commands for adding, listing, and removing invariants.
|
||
|
||
---
|
||
|
||
## CLI Reference
|
||
|
||
### `agents invariant add`
|
||
|
||
Create a new invariant constraint.
|
||
|
||
```bash
|
||
agents invariant add <NAME> --description <DESC> [SCOPE_FLAG]
|
||
```
|
||
|
||
**Scope flags:**
|
||
|
||
| Flag | Scope | Description |
|
||
|------|-------|-------------|
|
||
| `--global` | `GLOBAL` | Applies to every plan in the system |
|
||
| `--project <NAME>` | `PROJECT` | Applies to plans targeting the named project |
|
||
| `--plan <PLAN_ID>` | `PLAN` | Attached directly to a specific plan |
|
||
| `--action <ACTION>` | `ACTION` | Defined in an action template; promoted on `plan use` |
|
||
|
||
**Examples:**
|
||
|
||
```bash
|
||
# Global invariant
|
||
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"
|
||
```
|
||
|
||
---
|
||
|
||
### `agents invariant list`
|
||
|
||
Display invariants, optionally filtered by scope or project.
|
||
|
||
```bash
|
||
agents invariant list [PATTERN] [OPTIONS]
|
||
```
|
||
|
||
**Options:**
|
||
|
||
| Flag | Description |
|
||
|------|-------------|
|
||
| `--global` | Show only global invariants |
|
||
| `--project <NAME>` | Show only invariants for the named project |
|
||
| `--effective --project <NAME>` | Show the merged effective set for a project |
|
||
| `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |
|