# 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 [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 [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 --description [SCOPE_FLAG] ``` **Scope flags:** | Flag | Scope | Description | |------|-------|-------------| | `--global` | `GLOBAL` | Applies to every plan in the system | | `--project ` | `PROJECT` | Applies to plans targeting the named project | | `--plan ` | `PLAN` | Attached directly to a specific plan | | `--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 ` | Show only invariants for the named project | | `--effective --project ` | Show the merged effective set for a project | | `--format`, `-f` | Output format: `json`, `yaml`, `plain`, `table`, `rich` |