# CleverAgents Documentation (Detailed Spec) ## Key Themes Key themes include: * A **four-phase plan lifecycle** (Action → Strategize → Execute → Apply). * **Actors** as a unifying abstraction (an LLM/agent *or* a whole graph). * A **sandbox + diff review** workflow and **CLI-first** interaction model. * A scalable **context/memory architecture** (hot/warm/cold tiers, per-actor views). * **Invariants** as first-class constraints (global, project, action, and plan scoped) that flow into the decision tree, with precedence-based conflict resolution via the Invariant Reconciliation Actor. * A future-facing correction model where the user can "edit the decision tree" and only recompute affected subtrees. ## Big Picture: What CleverAgents *is* CleverAgents is your **command center for AI agents**—a unified platform for orchestrating any task you want agents to accomplish, from developing large software projects to writing comprehensive technical papers, administering databases, managing cloud infrastructure, or any complex multi-step workflow. The core value proposition is enabling **long-running, complex, large-scale tasks to execute autonomously with minimal human intervention**, making it ideal for building entire software systems, producing extensive documentation, or managing sophisticated operations largely hands-off. In **server mode**, CleverAgents becomes a collaborative hub where teams can share resources—prompts, actors, actions, and projects—while executing plans in the cloud. This enables a consistent experience across all your devices: start a complex task on your laptop, check progress from your phone, and review results from any machine. While CleverAgents leverages LangGraph and LangChain for the underlying LLM runtime primitives (tool calling, graphs, routing), its value lies in what it builds on top: **CleverAgents** provides: * A **first-class plan lifecycle** (Action/Strategize/Execute/Apply) for breaking down and tracking complex work, * A **project + resource model** for grounding tasks in real codebases, databases, documents, and infrastructure, * A consistent **actor abstraction** for defining and composing intelligent agents, * An independently registered **resource abstraction** for representing anything that can be read, written, or queried, * An independently registered **tool abstraction** for reusable, callable operations with resource bindings, * A consistent **skill abstraction** for organizing tools into composable capability collections, * A **sandbox + checkpoint** safety model for safe, reversible execution, * A **CLI/TUI/Web UX** for controlling and monitoring large multi-step autonomous work. ## Glossary (Terms Used Precisely) * **Plan**: A tracked lifecycle for a single unit-of-work (which may spawn child plans). Plans follow the same namespace rules as actors. * **Action**: A reusable plan template not tied to any project yet. Created via CLI commands with a required `--config` YAML configuration file that fully defines the action. CLI options provided alongside the config act as optional overrides. Actions follow the same namespace rules as actors. * **Strategize**: Read-only planning phase that produces a strategy and child plan blueprint. **All decisions are made during this phase.** * **Execute**: Phase that performs work in a sandbox; **spawns child plans** (based on decisions made in Strategize); produces artifacts/diffs. * **Apply**: Phase that commits sandbox results into the real project (and records an "applied" plan state). * **Project**: A collection of **linked resources** + configuration that define "where work happens" and "what can be touched." Created via CLI commands. Projects link to independently registered resources from the Resource Registry (they do not define resources inline). A resource can be linked to multiple projects. Can be **local** (contains local-only resources) or **remote** (all resources remotely accessible). * **Resource**: A namespaced, independently registered entity representing anything that can be read, written, or queried. Resources are managed through the `agents resource` CLI commands and stored in the **Resource Registry**. Each resource has a **resource type** that determines its properties, allowed parent/child relationships, sandbox strategy, and handler. Resources form a **directed acyclic graph (DAG)** with parent/child relationships. Resources are either **physical** (a specific, concrete manifestation — THIS file at THIS path, THIS git repository at THIS URL, THIS commit in THIS repo) or **virtual** (an abstract identity linking equivalent physical resources — "these two `fs-file` resources and this `git-tree-entry` resource all represent the same file with the same content, name, and permissions"). Two physical resources share a virtual parent when they are equivalent by the virtual type's criteria; when equivalence breaks, the virtual relationship is updated. Resources follow the same `/` naming convention as other entities. **Extends the MCP resource concept** to support both read and write operations. * **Resource Type**: A schema-level definition that constrains a category of resources. A resource type defines: what CLI arguments `agents resource add ` accepts, whether instances are physical or virtual, what child/parent resource types are allowed, what child resources are auto-discovered when an instance is created, the sandbox strategy, and the resource handler implementation. Built-in physical types span three layers: git metadata (`git`, `git-remote`, `git-branch`, `git-tag`, `git-commit`, `git-tree`, `git-tree-entry`, `git-stash`, `git-submodule`), git checkout (`git-checkout`), and filesystem (`fs-mount`, `fs-directory`, `fs-file`, `fs-symlink`, `fs-hardlink`). Built-in virtual types (`file`, `directory`, `symlink`, `commit`, `branch`, `tag`, `remote`, `submodule`, `tree`) link equivalent physical resources through content/identity matching. Custom types are defined in YAML configuration files and registered via `agents resource type add`. Resource types also specify whether instances can be created directly by users (`user_addable: true`) or are only auto-generated as children of other resources. * **Physical Resource**: A resource representing a specific, concrete manifestation — a particular file at a particular path, a particular git repository at a particular URL, a particular commit in a particular repo. Physical resources exist somewhere (locally on disk, or remotely on a server) and can be directly read and written by tools. Every physical resource is a distinct instance, even if its content is identical to another physical resource. A physical resource's parents can be either physical or virtual resources. * **Virtual Resource**: A resource representing an abstract identity that links equivalent physical resources. Virtual resources have no location of their own — they serve as shared parents of physical resources that are "the same" by some equivalence criterion (same content, same name, same permissions, same hash, etc.). Not all physical resources have a virtual parent. The children of a virtual resource can be physical resources, other virtual resources, or both. Built-in virtual types use simple names that mirror their physical counterparts: `file` (links `fs-file` + `git-tree-entry` with same content, filename, and permissions), `directory` (links `fs-directory` + `git-tree` with same recursive content), `symlink` (links `fs-symlink` + `git-tree-entry` mode 120000 with same target), `commit` (links `git-commit` across repos with same commit hash), `branch` (links `git-branch` across repos with same name and HEAD), `tag` (links `git-tag` across repos with same name and target), `remote` (links `git-remote` across repos with same URL), `submodule` (links `git-submodule` across repos with same URL and path), `tree` (links `git-tree` across repos with same tree hash). * **Resource Binding**: The declared association between a tool and the resources it operates on. A tool declares **resource slots** — typed placeholders specifying what resource types the tool requires (e.g., "I need a `git-checkout` resource with read_write access"). Slots are resolved to actual resources at activation time (**contextual binding** — from the plan's project), at registration time (**static binding** — hardcoded to a specific resource), or at invocation time (**parameter binding** — passed as a tool argument). * **Resource Registry**: A persistent catalog of all independently registered resources and their DAG relationships (parent/child links). Managed via `agents resource add/remove/list/show` CLI commands. The Resource Registry works alongside the Tool Registry and Skill Registry. * **Skill**: A namespaced, reusable collection of **tools** registered in the system via its own YAML configuration file and managed through the `agents skill` CLI commands. A skill assembles tools by referencing named tools from the Tool Registry, defining anonymous inline tools, and/or including other skills (whose tools are merged in). Skills can also expose tools from MCP servers, Agent Skills Standard folders, and built-in tool groups. When referencing named tools or including sub-skills, metadata can optionally be overridden. Actors reference skills by fully-qualified name. Extends the **MCP standard** and **Agent Skills standard**. Skills follow the same namespace rules as actors, tools, actions, etc. * **Tool**: A namespaced, independently registered, callable operation. A tool is the atomic unit of execution — it has a name, a JSON Schema for inputs/outputs, capability metadata (read-only, writes, checkpointable, etc.), and a uniform lifecycle (`discover`, `activate`, `execute`, `deactivate`). Tools are defined in their own YAML configuration files and managed through the `agents tool` CLI commands, following the same `/` naming convention as other entities. Tools originate from one of four sources: **MCP servers** (exposed via JSON-RPC `tools/call`), **Agent Skills Standard** folders (instruction-driven `SKILL.md` bundles), **built-in** operations (first-party file, git, search, and directory operations), or **custom** code (Python). Tools have a **dual role**: they can be included in skills (by reference) to form reusable capability collections, and they can be used directly as **tool nodes** in actor graphs. When referencing a named tool in a skill or actor, its metadata can optionally be overridden at the point of use. * **Anonymous Tool**: An inline tool definition (Python code block) that appears directly inside a skill YAML or an actor graph node. Anonymous tools use the same format as a named tool's YAML definition but lack a namespaced name — they are not registered in the Tool Registry, cannot be reused across skills or actors, and exist only within the scope where they are defined. Anonymous tools are useful for one-off, context-specific operations. * **Actor**: Anything conversational; may be a single agent/LLM or an entire graph of actors/tools. Defined via **YAML configuration files** (LangGraph definitions). Always named using `/` format. * **Session**: A user interaction context and conversation thread that can span multiple plans. * **Server**: Optional shared service for multi-user storage, permissions, and orchestration. Plans on remote projects can execute on the server. * **Namespace**: Scoping mechanism for actors, tools, skills, resources, resource types, actions, plans, automation profiles, etc. `local/` is reserved for local-only items. User namespaces (`/`) and organization namespaces (`/`) are stored on the server. Built-in LLM actors use provider namespaces (e.g., `openai/`, `anthropic/`). Built-in resource types use no namespace prefix (e.g., `git`, `git-checkout`, `fs-mount`, `fs-directory`, `file`, `commit`). * **Decision**: A recorded choice point made during Strategize that affects downstream work. Decisions form a tree structure that enables correction and replay. * **Invariant**: A named constraint or rule that applies to plan execution. Invariants can be attached at four scopes: **global** (applies to all plans), **project** (applies to all plans targeting that project), **plan** (applies to a specific plan and its child plans), or **action** (carried forward as plan-level invariants when the action is used). Managed via the unified `agents invariant add/list/remove` command, or attached at creation time via `--invariant` flags on `agents project create`, `agents action create`, and `agents plan use`. **Precedence**: plan-level invariants override project-level, which override global-level, when conflicts exist. An **Invariant Reconciliation Actor** (set via `--invariant-actor` on actions, plans, and projects, or globally via `agents config set invariant-actor`) resolves conflicts and computes the effective invariant view when a plan enters Strategize. During Strategize, the effective invariants are recorded as `invariant_enforced` decisions in the decision tree. When a top-level plan spawns child plans, the parent's effective invariant view is passed down to each child plan. * **Automation Profile**: A named collection of boolean flags that controls which tasks are automated vs. require human approval during plan execution. Built-in profiles (`locked-down`, `manual`, `supervised`, `trusted`, `autonomous`, `full-auto`) cover the spectrum from maximum human control to full automation. Custom profiles are defined in YAML configuration files, registered via `agents automation-profile add`, and follow the same `/` naming convention as other entities. Profiles control phase transitions, decision automation, validation self-fix behavior, strategy revision, child plan spawning, and safety requirements. * **ULID**: Universally Unique Lexicographically Sortable Identifier. Preferred over UUID for plan and decision IDs due to time-sortability. ## CLI Commands ### Command Synopsis

agents|cleveragents [--data-dir <DATA_PATH>] [--config-path <CONFIG_PATH>]
                     [--format (rich|color|table|plain|json|yaml)]
                     [--help|-h] [--version]
                     [--install-completion [<INST_SHELL>]] [--show-completion [<SHOW_SHELL>]]
                     [-v...] <COMMAND> [<ARGS>...]

agents version
agents info
agents diagnostics
agents init [--yes|-y]

agents session create [--actor <ACTOR>]
agents session list
agents session show <SESSION_ID>
agents session delete [--yes|-y] <SESSION_ID>
agents session export [(--output|-o) <FILE>] <SESSION_ID>
agents session import (--input|-i) <FILE>
agents session tell --session <SESSION_ID> [--actor <ACTOR>] [--stream] <PROMPT>

agents project create [(--description|-d) <DESC>] [--resource <RESOURCE>]...
                     [--invariant <INVARIANT>]... [--invariant-actor <ACTOR>] <NAME>
agents project link-resource [--read-only] <PROJECT> <RESOURCE>
agents project unlink-resource [--yes|-y] <PROJECT> <RESOURCE>
agents project list [(--namespace|-n) NS] [<REGEX>]
agents project show <PROJECT>
agents project validation add [(--description|-d) <DESC>] [--required|--informational]
                              [--timeout <SECONDS>] [--resource <RESOURCE>] <PROJECT> <COMMAND>
agents project validation remove [--yes|-y] <PROJECT> <VALIDATION_ID>
agents project validation list <PROJECT>
agents project delete [--force|-f] [--yes|-y] <NAME>
agents project context set[--view (strategize|execute|apply|default)]
                          [--include-resource <INCLUDE_RESOURCE>]...
                          [--exclude-resource <EXCLUDE_RESOURCE>]...
                          [--include-path <INCLUDE_GLOB>]...
                          [--exclude-path <EXCLUDE_GLOB>]...
                          [--hot-max-tokens <N>]
                          [--warm-max-decisions <N_WARM_MAX>]
                          [--cold-max-decisions <N_COLD_MAX>]
                          [--query-limit <N>]
                          [--max-file-size <MAX_FILE_BYTES>]
                          [--max-total-size <MAX_TOTAL_BYTES>]
                          [--summarize|--no-summarize]
                          [--summary-max-tokens <N>]
                          [--clear] <PROJECT>
agents project context show [--view (strategize|execute|apply|default)]
                            <PROJECT>

agents actor run [(--output|-o) <OUTPUT_FILE>]
                 [-v...] [--unsafe|-u] [--context <CONTEXT_NAME>]
                 [--context-dir <CONTEXT_PATH>] [--load-context <LOAD_CONTEXT_NAME>]
                 [(--temperature|-t) <TEMP>] [--allow-rxpy-in-run-mode]
                 [--skill <SKILL>]... <NAME> <PROMPT>
agents actor add (--config|-c) <FILE> [--unsafe] [(--option|-o) <key>=<value>]...
                [--type (graph|agent)] [--update]
                [--skill <SKILL>]... [(--description|-d) <DESC>] [<NAME>]
agents actor remove <NAME>
agents actor list
agents actor show <NAME>
agents actor context rm [--yes|-y] (--all|-a|<NAME>)
agents actor context list [<REGEX>]
agents actor context show <NAME>
agents actor context export (--output|-o) <FILE> <NAME>
agents actor context import [--update] (--input|-i) <FILE> [<NAME>]
agents actor context delete [--yes|-y] (--all|-a|<NAME>)
agents actor context clear [--yes|-y] (--all|-a|<NAME>)

agents skill add (--config|-c) <FILE> [(--description|-d) <DESC>] [--update]
                [--tool <TOOL>]... [--include-skill <INCLUDE_SKILL>]... [--mcp-server <SPEC>]... [<NAME>]
agents skill remove [--yes|-y] <NAME>
agents skill list [(--namespace|-n) <NS>] [--source <SOURCE>]
agents skill show  <NAME>
agents skill tools <NAME>

agents tool add (--config|-c) <FILE> [(--description|-d) <DESC>] [--update]
               [--source <SOURCE>] [--input-schema <JSON>] [--code <CODE>]
               [--writes|--no-writes] [--checkpointable|--no-checkpointable] [<NAME>]
agents tool remove [--yes|-y] <NAME>
agents tool list [(--namespace|-n) <NS>] [--source <SOURCE>] [<REGEX>]
agents tool show <NAME>

agents resource type add (--config|-c) <FILE> [--update]
                       [--physical|--virtual] [--user-addable|--no-user-addable]
                       [--sandbox-strategy <STRATEGY>] [--handler <NAME>]
                       [--child-type spec]... [--cli-arg spec]... [<NAME>]
agents resource type remove [--yes|-y] <NAME>
agents resource type list [<REGEX>]
agents resource type show <NAME>

agents resource add [(--description|-d) <DESC>] [--update] <TYPE> <NAME> [type-specific-flags...]
agents resource remove [--yes|-y] <NAME>
agents resource list [(--namespace|-n) <NS>] [(--type|-t) <TYPE>] [<REGEX>]
agents resource show <NAME>
agents resource tree [(--depth|-d) <N>] [(--type|-t) <TYPE>] <NAME>
agents resource link-child <PARENT> <CHILD>
agents resource unlink-child [--yes|-y] <PARENT> <CHILD>

agents plan list [--phase <PHASE>] [--state <STATE>] [--project <PROJECT>]
                [--action <ACTION>] [<REGEX>]
agents plan use [--automation-profile <PROFILE>]
                [--invariant <INVARIANT>]...
                [--strategy-actor <STRATEGY_ACTOR>]
                [--execution-actor <EXEC_ACTOR>]
                [--estimation-actor <EST_ACTOR>]
                [--invariant-actor <INV_ACTOR>]
                [--arg/-a name=value]...
                <ACTION> <PROJECT>...
agents plan execute <PLAN_ID>
agents plan apply [--yes|-y] <PLAN_ID>
agents plan status <PLAN_ID>
agents plan cancel [(--reason|-r) <REASON>] <PLAN_ID>
agents plan tree [--show-superseded] <PLAN_ID>
agents plan explain [--show-context] [--show-reasoning] <DECISION_ID>
agents plan correct --mode (revert|append) (--guidance|-g) <GUIDANCE>
                   [--dry-run] [--yes|-y] <DECISION_ID>
agents plan diff (--correction <CORRECTION_ATTEMPT_ID>|<PLAN_ID>)
agents plan artifacts <PLAN_ID>
agents plan prompt <PLAN_ID> <GUIDANCE>
agents plan rollback [--yes|-y] <PLAN_ID> <CHECKPOINT_ID>

agents action create (--config|-c) <CFG_FILE>
                     [--strategy-actor <STRATEGY_ACTOR>]
                     [--execution-actor <EXEC_ACTOR>]
                     [--definition-of-done <DOD>]
                     [(--description|-d) <DESC>]
                     [--long-description <LONG_DESC>]
                     [--reusable|--no-reusable]
                     [--read-only]
                     [--available]
                     [--estimation-actor <EST_ACTOR>]
                     [--invariant-actor <INV_ACTOR>]
                     [--automation-profile <PROFILE>]
                     [--invariant <INVARIANT>]...
                     [--arg <ARG_SPEC>]...
                     [<NAME>]
agents action list [(--namespace|-n) <NS>] [(--state|-s) <STATE>] [--available] [<REGEX>]
agents action show <ACTION_NAME>
agents action available <ACTION_NAME>
agents action archive <ACTION_NAME>

agents automation-profile add (--config|-c) <FILE> [--update] [<NAME>]
agents automation-profile remove [--yes|-y] <NAME>
agents automation-profile list [<REGEX>]
agents automation-profile show <NAME>

agents config set <key> <value>
agents config get <key>
agents config list [--filter-values <REGEX>] [<REGEX>]

agents invariant add [--global] [(--project|-p) PROJECT] [--plan PLAN_ID]...
                     [--action ACTION]... <INVARIANT_TEXT>
agents invariant list [--global] [(--project|-p) PROJECT] [--plan PLAN_ID] [--action ACTION]
                     [--effective] [<REGEX>]
agents invariant remove [--yes|-y] <INVARIANT_ID>
### Command Reference #### Global Options **Purpose** Configure global state locations and shell integration for every command. **Arguments** - `--data-dir PATH`: Overrides the global data directory (database, caches, sessions, logs). When omitted, the default data location is used. - `--config-path PATH`: Overrides the global configuration file path. When omitted, the default config path is used. - `--format rich|color|table|plain|json|yaml`: Set the output rendering format for all subcommands. When omitted, the value is read from the global config key `format`. If not set in config either, defaults to `rich`. See **Output Rendering Framework** for full details on each format. The available formats are: - `rich` (default): Uses modern rich CLI elements with dynamic effects, animated spinners, progress bars, and generous color. Best for interactive terminal use. - `color`: Plain scrolling text with ANSI color codes. Good for terminals that support color but not advanced rendering. - `table`: ASCII box-drawing characters to create structured tables and panels with color. Similar to the examples shown throughout this document. - `plain`: Plain text with no color codes or non-ASCII characters. Suitable for piping into files, logs, or non-terminal consumers. - `json`: Structured JSON output for programmatic consumption. No color except within verbatim text values. - `yaml`: Structured YAML output for programmatic consumption. No color except within verbatim text values. - `--help`, `-h`: Print help for the current command. - `--version`, `-V`: Print the version and exit. - `--install-completion [SHELL]`: Install shell completion for the given shell. - `--show-completion [SHELL]`: Show the completion script for the given shell. **Examples**

$ agents --data-dir /srv/cleveragents --config-path /srv/cleveragents/config.toml info

╭─ System Snapshot ─────────────────╮
│ CleverAgents 1.0.0                │
│ Mode: local                       │
│ Automation: review                │
│ Status: ready                     │
╰───────────────────────────────────╯

╭─ Paths ───────────────────────────────╮
│ Data Dir: /srv/cleveragents           │
│ Config: /srv/cleveragents/config.toml │
│ Logs: /srv/cleveragents/logs          │
│ Cache: /srv/cleveragents/cache        │
│ Database: /srv/cleveragents/agents.db │
╰───────────────────────────────────────╯

╭─ Runtime ──────────╮
│ PID: 4127          │
│ Uptime: 00:14:32   │
│ Python: 3.13.1     │
│ Host: devbox.local │
│ Platform: linux    │
╰────────────────────╯

╭─ Projects & Sessions ─╮
│ Projects: 2           │
│ Sessions: 1 active    │
│ Active Plans: 0       │
│ Actors: 3             │
╰───────────────────────╯

✓ OK Environment loaded
#### agents version **Purpose** Print the current CLI version. **Arguments** None. **Examples**

$ agents version

╭─ CLI Version ────╮
│ CleverAgents CLI │
│ Version: 1.0.0   │
│ Channel: stable  │
│ Python: 3.13     │
╰──────────────────╯

╭─ Build ────────────────╮
│ Build Date: 2026-02-08 │
│ Commit: a17c3f9        │
│ Schema: v3             │
│ Platform: linux-x86_64 │
╰────────────────────────╯

╭─ Dependencies ─────────────────╮
│ LangGraph: 0.2.60              │
│ LangChain: 0.3.18              │
│ MCP SDK: 1.4.0                 │
│ Pydantic: 2.10.4               │
╰────────────────────────────────╯

✓ OK Version reported
#### agents info **Purpose** Show configuration and runtime information useful for debugging or support. **Arguments** None. **Examples**

$ agents info

╭─ Environment ───────────────────────────────────────────────╮
│ Data Dir: /home/alex/.cleveragents                          │
│ Config: /home/alex/.cleveragents/config.toml                │
│ Database: sqlite:///home/alex/.cleveragents/cleveragents.db │
│ Server Mode: disabled                                       │
│ Platform: Linux 6.8.0 (x86_64)                              │
╰─────────────────────────────────────────────────────────────╯

╭─ Runtime ─────────────────────────╮
│ Automation: review                │
│ Providers: 3 configured           │
│ Sessions: 2 active                │
│ Active Plans: 1                   │
╰───────────────────────────────────╯

╭─ Storage ─────╮
│ Cache: 118 MB │
│ Logs: 42 MB   │
│ Backups: 3    │
│ DB Size: 8 MB │
╰───────────────╯

╭─ Indexing ─────────────╮
│ Text Index: ready      │
│ Vector Index: ready    │
│ Graph Store: disabled  │
│ Indexed Files: 1,247   │
╰────────────────────────╯

✓ OK Environment details ready
#### agents diagnostics **Purpose** Run health checks for configuration, providers, and filesystem permissions. **Arguments** None. **Examples**

$ agents diagnostics

╭─ Checks ────────────────────────────────╮
│ Check            Status  Details        │
│ ───────────────  ──────  ────────────── │
│ Config file      OK      readable       │
│ Database         OK      writable       │
│ OPENAI_API_KEY   WARN    missing        │
│ Anthropic key    OK      configured     │
│ Disk space       OK      2.1 GB free    │
│ Text index       OK      tantivy 0.22   │
│ Vector index     OK      faiss (CPU)    │
│ Graph store      WARN    not configured │
│ File permissions OK      data dir r/w   │
│ Git              OK      git 2.43.0     │
╰─────────────────────────────────────────╯

╭─ Summary ─────────╮
│ Checks: 10 total  │
│ Warnings: 2       │
│ Errors: 0         │
│ Duration: 0.6s    │
╰───────────────────╯

╭─ Recommendations ─────────────────────────────────────────────╮
│ - Set OPENAI_API_KEY to enable OpenAI models                  │
│ - Configure a graph store backend for structural code queries │
│ - Verify provider credentials via config                      │
╰───────────────────────────────────────────────────────────────╯

⚠ WARN 2 warnings require attention
When critical checks fail, diagnostics reports errors:

$ agents diagnostics

╭─ Checks ────────────────────────────────────────────╮
│ Check            Status   Details                   │
│ ───────────────  ───────  ────────────────────      │
│ Config file      OK       readable                  │
│ Database         ERROR    locked by another process │
│ OPENAI_API_KEY   OK       configured                │
│ Anthropic key    ERROR    invalid key format        │
│ Disk space       WARN     312 MB free (low)         │
│ Text index       OK       tantivy 0.22              │
│ Vector index     ERROR    FAISS library not found   │
│ Graph store      OK       neo4j 5.15                │
│ File permissions OK       data dir r/w              │
│ Git              OK       git 2.43.0                │
╰─────────────────────────────────────────────────────╯

╭─ Summary ─────────╮
│ Checks: 10 total  │
│ Warnings: 1       │
│ Errors: 3         │
│ Duration: 1.2s    │
╰───────────────────╯

╭─ Errors (must fix) ─────────────────────────────────────────────────╮
│ 1. Database is locked by PID 12847 — stop the other process or      │
│    delete the lock file at ~/.cleveragents/agents.db-lock           │
│ 2. Anthropic key starts with "pk-" — expected "sk-ant-" prefix      │
│    Run: agents config set anthropic-api-key                         │
│ 3. FAISS library not installed — vector search will not work        │
│    Run: pip install faiss-cpu                                       │
╰─────────────────────────────────────────────────────────────────────╯

✗ ERROR 3 errors must be resolved before CleverAgents can operate
#### agents init **Purpose** Initialize or reset the global CleverAgents environment. This wipes any existing data and re-creates the global config and database. **Arguments** - `--yes`: Skip the confirmation prompt and proceed with the wipe. **Examples**

$ agents init

Warning: This will remove all data in /home/alex/.cleveragents
Continue? [y/N]: y

╭─ Environment Reset ────────────────────────────────╮
│ Config: /home/alex/.cleveragents/config.toml       │
│ Database: /home/alex/.cleveragents/cleveragents.db │
│ Backup: /home/alex/.cleveragents.backup-2026-02-08 │
│ Status: ready                                      │
╰────────────────────────────────────────────────────╯

╭─ Defaults ──────────────────────────────────╮
│ Automation Profile: supervised              │
│ Built-in Profiles: 6 loaded                 │
╰─────────────────────────────────────────────╯

╭─ Created ─────────────────╮
│ Config: config.toml       │
│ Database: cleveragents.db │
│ Logs: logs/               │
│ Cache: cache/             │
│ Backups: backups/         │
╰───────────────────────────╯

╭─ Schema ───────────────────────╮
│ Version: v3                    │
│ Tables: 12 created             │
│ Migrations: up to date         │
╰────────────────────────────────╯

✓ OK Environment initialized
Non-interactive initialization using `--yes` (useful in scripts and CI):

$ agents init --yes

╭─ Initialized ──────────────────────────────────────────╮
│ Data Dir: /home/alex/.cleveragents (created)           │
│ Config: /home/alex/.cleveragents/config.toml           │
│ Database: initialized (schema v3)                      │
│ Directories: logs, cache, sessions, contexts           │
╰────────────────────────────────────────────────────────╯

✓ OK Initialized (non-interactive)
#### agents session **Purpose** Manage interactive sessions that hold a conversation history and orchestrator state. ##### agents session create **Purpose** Create a new session for interactive work. **Arguments** - `--actor ACTOR`: Orchestrator actor to use for `session tell`. **Examples**

$ agents session create --actor local/orchestrator

╭─ Session ───────────────────────╮
│ ID: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z  │
│ Actor: local/orchestrator       │
│ Created: 2026-02-08 12:44       │
│ Namespace: local                │
╰─────────────────────────────────╯

╭─ Settings ─────────────╮
│ Automation: review     │
│ Streaming: off         │
│ Context: default       │
│ Memory: enabled        │
│ Max History: 50 turns  │
╰────────────────────────╯

╭─ Actor Details ───────────────────╮
│ Provider: anthropic               │
│ Model: claude-3.5                 │
│ Temperature: 0.7                  │
│ Context Window: 200K tokens       │
╰───────────────────────────────────╯

✓ OK Session created
##### agents session list **Purpose** List sessions available on the local machine. **Arguments** None. **Examples**

$ agents --format table session list

╭─ Sessions ───────────────────────────────────────────────────────────────────╮
│ ID        Name             Actor               Messages  Updated             │
│ ────────  ───────────────  ──────────────────  ────────  ────────────────    │
│ 01HXM2A6  weekly-planning  local/orchestrator  6         2026-02-08 12:44    │
│ 01HXM1F2  refactor-sprint  local/orchestrator  14        2026-02-07 18:11    │
╰──────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ────────────────────╮
│ Total: 2                     │
│ Most Recent: weekly-planning │
│ Oldest: refactor-sprint      │
│ Total Messages: 20           │
│ Storage: 42 KB               │
╰──────────────────────────────╯

✓ OK 2 sessions listed
##### agents session show **Purpose** Show details and recent messages for a session. **Arguments** - ``: The session identifier. **Examples**

$ agents session show 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

╭─ Session Summary ───────────────╮
│ ID: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z  │
│ Actor: local/orchestrator       │
│ Messages: 6                     │
│ Created: 2026-02-08 12:30       │
│ Updated: 2026-02-08 12:44       │
│ Automation: review              │
╰─────────────────────────────────╯

╭─ Recent Messages ──────────────────────────────────╮
│ user  Create an action to refresh dependency locks │
│ assistant  Plan created, running commands...       │
│ assistant  Completed 2 commands                    │
╰────────────────────────────────────────────────────╯

╭─ Linked Plans ────────────────────────────────╮
│ Plan ID                     Phase   State     │
│ ──────────────────────────  ──────  ────────  │
│ 01HXM8C2ZK4Q7C2B3F2R4VYV6J  execute  complete │
╰───────────────────────────────────────────────╯

╭─ Token Usage ──────────────╮
│ Input Tokens: 3,420        │
│ Output Tokens: 1,185       │
│ Estimated Cost: $0.0184    │
╰────────────────────────────╯

✓ OK Session details loaded
##### agents session delete **Purpose** Delete a session and its stored conversation history. **Arguments** - ``: The session identifier. - `--yes, -y`: Skip the confirmation prompt. **Examples**

$ agents session delete 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

Delete session 01HXM2A6K1P2E9Q9D4GQ7J4S7Z? [y/N]: y

╭─ Deletion Summary ──────────────────╮
│ Session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z │
│ ID: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z      │
│ Messages: 6 removed                 │
│ Storage: 18 KB freed                │
│ Plans Orphaned: 0                   │
╰─────────────────────────────────────╯

╭─ Cleanup ───────────╮
│ Backups: none       │
│ Logs: preserved     │
│ Context: cleared    │
│ Checkpoints: none   │
╰─────────────────────╯

✓ OK Session deleted
##### agents session export **Purpose** Export a session as a portable JSON file. **Arguments** - ``: The session identifier. - `--output/-o FILE`: Output file path (optional). **Examples**

$ agents session export --output /tmp/weekly-planning.json 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

╭─ Session Export ────────────────────╮
│ Session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z │
│ Output: /tmp/weekly-planning.json   │
│ Messages: 6                         │
│ Size: 24 KB                         │
│ Format: JSON                        │
╰─────────────────────────────────────╯

╭─ Contents ─────────────────╮
│ Messages: 6                │
│ Plan References: 1         │
│ Metadata Keys: 2           │
│ Actor Config: included     │
│ Schema Version: v3         │
╰────────────────────────────╯

╭─ Integrity ──────────────────╮
│ Checksum: sha256:7a9b...42c1 │
│ Encrypted: no                │
╰──────────────────────────────╯

✓ OK Export completed
##### agents session import **Purpose** Import a session JSON file. **Arguments** - `--input/-i FILE`: Input JSON file. **Examples**

$ agents session import --input /tmp/weekly-planning.json

╭─ Session Import ────────────────────────╮
│ Input: /tmp/weekly-planning.json        │
│ Session ID: 01HXM3D3B2W4CQYQ3P4ZB8A5T1  │
│ Messages: 6                             │
│ Schema: v3                              │
╰─────────────────────────────────────────╯

╭─ Validation ────────────╮
│ Checksum: verified      │
│ Schema: compatible      │
│ Actor Ref: resolved     │
╰─────────────────────────╯

╭─ Merge ──────────────╮
│ Existing: none       │
│ Strategy: create new │
╰──────────────────────╯

✓ OK Import completed
##### agents session tell **Purpose** Send a natural-language request to the orchestrator. The orchestrator can create actions, plans, or project changes by issuing the necessary CleverAgents commands under the hood. **Arguments** - ``: Instruction text (positional argument). - `--session SESSION_ID`: Session to use (required). - `--actor ACTOR`: Override the session actor for this request. - `--stream`: Stream progress as the orchestrator works. **Examples**

$ agents session tell "Create an action to refresh dependency locks and add it to the platform project" \
  --session 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

╭─ Plan Request ──────────────────────────────────────────╮
│ Actor: local/orchestrator                               │
│ Session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z                     │
│ Automation: review                                      │
│ Prompt: Create an action to refresh dependency locks... │
╰─────────────────────────────────────────────────────────╯

╭─ Commands Executed ─────────────────────────────────────────────────────────────────────────────────────────╮
│ - agents action create --config ./actions/refresh-locks.yaml local/refresh-locks                            │
│ - agents resource add git-checkout local/platform-repo --path /repos/platform                               │
│ - agents project link-resource local/platform local/platform-repo                                           │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Result ────────────────────────────╮
│ Action: local/refresh-locks (draft) │
│ Project: local/platform             │
│ Resource: repo                      │
╰─────────────────────────────────────╯

╭─ Usage ─────────────────────╮
│ Input Tokens: 1,842         │
│ Output Tokens: 624          │
│ Cost: $0.0094               │
│ Duration: 3.2s              │
│ Tool Calls: 3               │
╰─────────────────────────────╯

✓ OK Orchestrator completed 3 commands
Using `--stream` to see the response as it is generated (token by token):

$ agents session tell --session 01HXM2A6K1 --stream "What files were changed in the last plan?"

╭─ Session ──────────────────────────╮
│ ID: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z     │
│ Actor: local/orchestrator          │
│ Mode: streaming                    │
╰────────────────────────────────────╯

 The last plan (01HXM8C2ZK) modified 6 files in the auth module:

  1. `src/auth/session.py` — refactored session validation
  2. `src/auth/tokens.py` — updated token expiry to 7200s
  3. `src/auth/__init__.py` — updated exports
  4. `tests/test_auth.py` — added 12 new test cases
  5. `tests/test_session.py` — updated session fixtures
  6. `docs/auth.md` — updated API documentation

All changes passed validation (24/24 tests, lint clean).

╭─ Usage ──────────────────╮
│ Tokens: 1,240 (stream)   │
│ Duration: 3.1s           │
│ Tool Calls: 2            │
╰──────────────────────────╯

✓ OK Stream complete
#### agents project **Purpose** Manage projects and their resources. ##### agents project create **Purpose** Create a new project record. **Arguments** - ``: Namespaced project name (positional argument). - `--description/-d TEXT`: Optional description. - `--resource RESOURCE`: Resource to link to the project at creation time (repeatable). - `--invariant TEXT`: Invariant to attach to this project (repeatable). These invariants apply to all plans targeting this project. - `--invariant-actor ACTOR`: Invariant Reconciliation Actor for this project. Used to reconcile project-level invariants against global invariants for all plans targeting this project (unless the plan overrides it). **Examples**

$ agents project create --description "Backend API" local/api-service

╭─ Project ──────────────────────╮
│ Name: local/api-service        │
│ ID: 01HXM4T08Y0N5R9VZ4QX4BPTZ1 │
│ Description: Backend API       │
│ Type: local                    │
│ Created: 2026-02-08 12:46      │
╰────────────────────────────────╯

╭─ Paths ────────────────────────────────────╮
│ Root: /repos/api-service                   │
│ Data Dir: /repos/api-service/.cleveragents │
╰────────────────────────────────────────────╯

╭─ Defaults ──────────────────────────────╮
│ Sandbox: git_worktree                   │
│ Validations: 0                          │
│ Context Filters: none                   │
│ Automation Profile: (inherits global)   │
╰─────────────────────────────────────────╯

╭─ Resources ──────╮
│ Total: 0         │
│ Indexed: 0       │
│ Sandboxable: 0   │
╰──────────────────╯

✓ OK Project created
Creating a project with resources and invariants in one command:

$ agents project create -d "Frontend web application" \
  --resource local/web-repo --resource local/staging-db \
  --invariant "All components must have unit tests" \
  --invariant "CSS must pass stylelint checks" \
  --invariant-actor local/invariant-resolver \
  local/web-app

╭─ Project Created ─────────────────────╮
│ Name: local/web-app                   │
│ ID: 01HXM4V19Z0N5R9VZ4QX4BPTZ1        │
│ Description: Frontend web app         │
│ Remote: no                            │
╰───────────────────────────────────────╯

╭─ Linked Resources ─────────────────────────────╮
│ Resource           Type          Read-Only     │
│ ───────────────────  ──────────────  ───────── │
│ local/web-repo       git-checkout    no        │
│ local/staging-db     local/database  no        │
╰────────────────────────────────────────────────╯

╭─ Invariants ────────────────────────────────────╮
│ 1. All components must have unit tests          │
│ 2. CSS must pass stylelint checks               │
│ Reconciliation Actor: local/invariant-resolver  │
╰─────────────────────────────────────────────────╯

✓ OK Project created
##### agents project link-resource **Purpose** Link a registered resource to a project. The resource must already be registered via `agents resource add`. A resource can be linked to multiple projects. Optionally, the resource can be marked as read-only within this project context, and given an alias for convenience. **Arguments** - ``: Project name (positional argument). - ``: Registered resource name (positional argument). - `--read-only`: Mark the resource as read-only within this project (even if the resource itself is writable). **Examples**

$ agents resource add git-checkout local/api-repo --path /repos/api --branch main
$ agents project link-resource local/api-service local/api-repo

╭─ Resource Linked ───────────────────────╮
│ Project: local/api-service              │
│ Resource: local/api-repo                │
│ Type: git-checkout                      │
│ Read-Only: no                           │
╰─────────────────────────────────────────╯

╭─ Permissions ────────────╮
│ Read: allowed            │
│ Write: allowed           │
│ Apply: requires approval │
╰──────────────────────────╯

╭─ Indexing ─────────────────────╮
│ Status: indexing...            │
│ Files Found: 347               │
│ Language: Python (primary)     │
│ Estimated Time: ~20 seconds    │
╰────────────────────────────────╯

✓ OK Resource linked to project
##### agents project unlink-resource **Purpose** Unlink a resource from a project. The resource itself remains registered in the Resource Registry and can still be linked to other projects. Active plans using this resource in this project context will be warned. **Arguments** - ``: Project name (positional argument). - ``: Resource name (positional argument). - `--yes, -y`: Skip confirmation prompt. **Examples**

$ agents project unlink-resource local/api-service local/api-repo

Unlink local/api-repo from local/api-service? [y/N]: y

╭─ Resource Unlinked ────────────────────╮
│ Project: local/api-service             │
│ Resource: local/api-repo               │
│ Type: git-checkout                     │
╰────────────────────────────────────────╯

╭─ Index Cleanup ──────────╮
│ Text Index: 347 removed  │
│ Vectors: 892 removed     │
│ Graph Triples: cleared   │
│ Duration: 0.3s           │
╰──────────────────────────╯

╭─ Project Summary ──────────────╮
│ Linked Resources: 1 remaining  │
│ Last Updated: 2026-02-09 10:30 │
│ Active Plans: 0                │
╰────────────────────────────────╯

✓ OK Resource unlinked from project
##### agents project list **Purpose** List projects with optional filters. **Arguments** - `--namespace/-n NS`: Filter by namespace. **Examples**

$ agents --format table project list

╭─ Projects ────────────────────────────────────────────────────────╮
│ ID        Name               Resources  Remote  Active Plans      │
│ ────────  ─────────────────  ─────────  ──────  ────────────      │
│ 01HXM4T0  local/api-service  2          No      1                 │
│ 01HXM4B9  local/docs         1          No      0                 │
╰───────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────────╮
│ Total: 2                   │
│ With Resources: 2          │
│ Remote: 0                  │
│ Total Resources: 3         │
│ Indexed Files: 1,247       │
│ Active Plans: 1            │
╰────────────────────────────╯

✓ OK 2 projects listed
##### agents project show **Purpose** Show full project details. **Arguments** - ``: Project name. **Examples**

$ agents project show local/api-service

╭─ Project Details ──────────────╮
│ Name: local/api-service        │
│ ID: 01HXM4T08Y0N5R9VZ4QX4BPTZ1 │
│ Description: Backend API       │
│ Resources: 2                   │
│ Remote: no                     │
│ Created: 2026-02-08 12:46      │
╰────────────────────────────────╯

╭─ Linked Resources ──────────────────────────────────────────────────────╮
│ Resource          Type            Sandbox              Read-Only        │
│ ────────────────  ──────────────  ────────────────────  ─────────       │
│ local/api-repo   git-checkout    git_worktree         no                │
│ local/staging-db local/database  transaction_rollback yes               │
╰─────────────────────────────────────────────────────────────────────────╯

╭─ Validations (3) ───────────────────────────────────────────╮
│ val_01HXM5A  pytest --cov=src --cov-fail-under=80  required │
│ val_01HXM5B  ruff check .                          required │
│ val_01HXM5C  node scripts/check-bundle-size.js     info     │
╰─────────────────────────────────────────────────────────────╯

╭─ Context ───────────────────╮
│ Include: repo               │
│ Exclude: **/node_modules/** │
│ Max File Size: 1 MB         │
╰─────────────────────────────╯

╭─ Indexing Status ──────────╮
│ Text Index: ready          │
│ Vector Index: ready        │
│ Graph Store: disabled      │
│ Indexed Files: 347         │
│ Last Indexed: 12:48        │
╰────────────────────────────╯

╭─ Active Plans ──────────────────────────╮
│ Plan ID   Action               Phase    │
│ ────────  ───────────────────  ───────  │
│ 01HXM7A9  local/code-coverage  execute  │
╰─────────────────────────────────────────╯

✓ OK Project loaded
##### agents project validation **Purpose** Manage validations for a project. Validations are commands that run during the Execute phase to verify the correctness of changes. Each validation has an optional description, a command to execute, and a mode (required or informational). Required validations must pass for execution to proceed; informational validations are included in the summary but do not block execution. ###### agents project validation add **Purpose** Add a validation to a project. **Arguments** - ``: Project name (positional argument). - ``: The shell command to run (positional argument, quoted string). - `--description/-d TEXT`: Optional description of what this validation checks. - `--required/--informational`: Whether the validation must pass (default: `--required`). Required validations block execution on failure; informational validations report results without blocking. - `--timeout SECONDS`: Timeout for this validation command (default: 300). - `--resource RESOURCE`: Scope this validation to a specific linked resource. **Examples**

$ agents project validation add --description "Run unit tests with coverage" \
  --required --timeout 600 --resource repo local/api-service "pytest --cov=src --cov-fail-under=80"

╭─ Validation Added ──────────────────────────────────────────╮
│ ID: val_01HXM5A                                             │
│ Project: local/api-service                                  │
│ Command: pytest --cov=src --cov-fail-under=80               │
│ Description: Run unit tests with coverage                   │
│ Mode: required                                              │
│ Resource: repo                                              │
│ Timeout: 600s                                               │
╰─────────────────────────────────────────────────────────────╯

✓ OK Validation added

$ agents project validation add --description "Lint check" \
  --required local/api-service "ruff check ."

╭─ Validation Added ───────────────────╮
│ ID: val_01HXM5B                      │
│ Project: local/api-service           │
│ Command: ruff check .                │
│ Description: Lint check              │
│ Mode: required                       │
│ Timeout: 300s                        │
╰──────────────────────────────────────╯

✓ OK Validation added

$ agents project validation add --description "Check bundle size (advisory)" \
  --informational local/api-service "node scripts/check-bundle-size.js"

╭─ Validation Added ───────────────────────────────╮
│ ID: val_01HXM5C                                  │
│ Project: local/api-service                       │
│ Command: node scripts/check-bundle-size.js       │
│ Description: Check bundle size (advisory)        │
│ Mode: informational                              │
│ Timeout: 300s                                    │
╰──────────────────────────────────────────────────╯

✓ OK Validation added
###### agents project validation remove **Purpose** Remove a validation from a project. **Arguments** - ``: Project name (positional argument). - ``: Validation identifier (positional argument). - `--yes, -y`: Skip confirmation prompt. **Examples**

$ agents project validation remove local/api-service val_01HXM5C

Remove validation val_01HXM5C from local/api-service? [y/N]: y

╭─ Validation Removed ──────────────────────────────╮
│ ID: val_01HXM5C                                   │
│ Command: node scripts/check-bundle-size.js        │
│ Description: Check bundle size (advisory)         │
╰───────────────────────────────────────────────────╯

✓ OK Validation removed
###### agents project validation list **Purpose** List all validations for a project. **Arguments** - ``: Project name (positional argument). **Examples**

$ agents project validation list local/api-service

╭─ Validations ─────────────────────────────────────────────────────────────────────────╮
│ ID           Command                              Mode          Timeout  Resource     │
│ ───────────  ───────────────────────────────────  ────────────  ───────  ────────     │
│ val_01HXM5A  pytest --cov=src --cov-fail-under=80  required      600s     repo        │
│ val_01HXM5B  ruff check .                          required      300s     (all)       │
│ val_01HXM5C  node scripts/check-bundle-size.js     informational 300s     (all)       │
╰───────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────╮
│ Total: 3               │
│ Required: 2            │
│ Informational: 1       │
╰────────────────────────╯

✓ OK 3 validations listed
##### agents project delete **Purpose** Delete a project and all associated resources. **Arguments** - ``: Project name. - `--force/-f`: Delete even if active plans exist. - `--yes`: Skip confirmation prompt. **Examples**

$ agents project delete local/docs

Delete project local/docs? This cannot be undone. [y/N]: y

╭─ Deletion Summary ──────────────────╮
│ Project: local/docs                 │
│ ID: 01HXM4B9F2C1V8X2N6Q7K9L0M1      │
│ Resources: 1 removed                │
│ Data Dir: /repos/docs/.cleveragents │
╰─────────────────────────────────────╯

╭─ Index Cleanup ────────╮
│ Text Index: cleared    │
│ Vectors: 240 removed   │
│ Graph Triples: none    │
│ Storage Freed: 12 MB   │
╰────────────────────────╯

╭─ Backups ────────────────────────────────────╮
│ Snapshot: /backups/local-docs-2026-02-08.tgz │
│ Retention: 7 days                            │
╰──────────────────────────────────────────────╯

✓ OK Project deleted
Attempting to delete a project that has active plans (without `--force`):

$ agents project delete local/api-service

Delete project local/api-service? [y/N]: y

╭─ Delete Blocked ──────────────────────────────────────────╮
│ Cannot delete: project has active plans                   │
│ Active Plans: 2                                           │
│   01HXM7A9 — execute/processing (local/code-coverage)     │
│   01HXM4J1 — strategize/processing (local/refactor-api)   │
╰───────────────────────────────────────────────────────────╯

╭─ Resolution ──────────────────────────────────────────────────────╮
│ - Cancel or complete active plans first, or                       │
│ - Use --force to cancel all active plans and delete the project   │
╰───────────────────────────────────────────────────────────────────╯

✗ ERROR Delete blocked — 2 active plans
Force-deleting a project with active plans:

$ agents project delete --force --yes local/api-service

╭─ Force Delete ─────────────────────────────────────────╮
│ Cancelling: 2 active plans                             │
│   01HXM7A9 — cancelled (was execute/processing)        │
│   01HXM4J1 — cancelled (was strategize/processing)     │
╰────────────────────────────────────────────────────────╯

╭─ Deleted ────────────────────╮
│ Project: local/api-service   │
│ Resources Unlinked: 2        │
│ Validations Removed: 3       │
│ Plans Cancelled: 2           │
│ Context Policies: cleared    │
╰──────────────────────────────╯

✓ OK Project force-deleted
##### agents project context **Purpose** Manage context policies for the hot/warm/cold tiers and per-view context selection. ###### agents project context set **Purpose** Set the context policy for a project and (optionally) a specific view. **Arguments** - ``: Project name (positional argument, at end of command). - `--view strategize|execute|apply|default`: Which view this policy applies to. - `--include-resource NAME`: Resource allowlist (repeatable). - `--exclude-resource NAME`: Resource denylist (repeatable). - `--include-path GLOB`: Path allowlist (repeatable). - `--exclude-path GLOB`: Path denylist (repeatable). - `--hot-max-tokens N`: Maximum token budget for hot context. This is a soft cap and may be null. The actor/LLM hard limit can be lower; the effective hot context is the lesser of the two. - `--warm-max-decisions N`: Maximum number of decisions kept in warm context. - `--cold-max-decisions N`: Maximum number of decisions kept in cold context. - `--query-limit N`: Max number of retrieval results per query. - `--max-file-size BYTES`: Max file size included in context. - `--max-total-size BYTES`: Max total size across included files. - `--summarize/--no-summarize`: Enable or disable summarization for large context segments. - `--summary-max-tokens N`: Token limit for generated summaries. - `--clear`: Clear the policy for the selected view. **Examples**

$ agents project context set --view strategize \
  --include-resource repo --exclude-path "**/node_modules/**" \
  --hot-max-tokens 12000 --warm-max-decisions 50 --cold-max-decisions 200 \
  --summarize --summary-max-tokens 800 local/api-service

╭─ Context Policy ────────────╮
│ Project: local/api-service  │
│ View: strategize            │
│ Include: repo               │
│ Exclude: **/node_modules/** │
╰─────────────────────────────╯

╭─ Limits ─────────────────────╮
│ Hot Tokens: 12000 (soft cap) │
│ Warm Decisions: 50           │
│ Cold Decisions: 200          │
│ Query Limit: 20              │
│ Max File Size: 1 MB          │
│ Max Total Size: 50 MB        │
╰──────────────────────────────╯

╭─ Summarization ─╮
│ Enabled: yes    │
│ Max Tokens: 800 │
╰─────────────────╯

╭─ Other Views ────────╮
│ execute: (default)   │
│ apply: (default)     │
│ default: (unset)     │
╰──────────────────────╯

✓ OK Context policy updated
###### agents project context show **Purpose** Show the active context policy for a project. **Arguments** - ``: Project name (positional argument, at end of command). - `--view strategize|execute|apply|default`: View to display. **Examples**

$ agents project context show --view strategize local/api-service

╭─ Context Policy ────────────╮
│ Project: local/api-service  │
│ View: strategize            │
│ Include: repo               │
│ Exclude: **/node_modules/** │
╰─────────────────────────────╯

╭─ Limits ─────────────────────╮
│ Hot Tokens: 12000 (soft cap) │
│ Warm Decisions: 50           │
│ Cold Decisions: 200          │
│ Query Limit: 20              │
│ Max File Size: 1 MB          │
│ Max Total Size: 50 MB        │
╰──────────────────────────────╯

╭─ Summarization ─╮
│ Enabled: yes    │
│ Max Tokens: 800 │
╰─────────────────╯

╭─ Current Usage ──────────────╮
│ Hot Context: 8,420 / 12,000  │
│ Warm Entries: 12 / 50        │
│ Cold Entries: 47 / 200       │
│ Indexed Resources: 1         │
╰──────────────────────────────╯

✓ OK Context policy loaded
#### agents actor **Purpose** Manage actors and run actor configurations directly. ##### agents actor run **Purpose** Run a named actor in isolation with simple, manual context. **Arguments** - ``: The name of the actor to run (required). - ``: Prompt text (positional argument). - `--output/-o FILE`: Output file path. - `--verbose/-v`: Increase verbosity (repeatable). - `--unsafe/-u`: Allow unsafe configs. - `--context NAME`: Named actor context to attach. - `--context-dir PATH`: Context storage location. - `--load-context FILE`: Load context from JSON. - `--temperature/-t FLOAT`: Override temperature. - `--skill NAME`: Skill to attach (repeatable). - `--allow-rxpy-in-run-mode`: Allow RxPy routes in run mode. **Examples**

$ agents actor run --context docs local/code_reader "Summarize the README"

╭─ Run Summary ─────────────────╮
│ Actor: local/code_reader      │
│ Context: docs                 │
│ Temperature: 0.2              │
│ Provider: anthropic           │
│ Model: claude-3.5             │
╰───────────────────────────────╯

╭─ Inputs ─────────────────────╮
│ Prompt: Summarize the README │
│ Context Files: 3             │
│ Context Size: 12.4 KB        │
╰──────────────────────────────╯

╭─ Result Metrics ───────╮
│ Output: stdout         │
│ Input Tokens: 1,524    │
│ Output Tokens: 842     │
│ Duration: 1.8s         │
│ Cost: $0.0021          │
│ Tool Calls: 0          │
╰────────────────────────╯

✓ OK Summary generated
Running an actor with a custom temperature and skill attachment:

$ agents actor run --temperature 0.2 --skill local/code-analysis \
  local/reviewer "Review the auth module for security issues"

╭─ Actor Run ─────────────────────────╮
│ Actor: local/reviewer               │
│ Type: agent                         │
│ Temperature: 0.2                    │
│ Skill: local/code-analysis          │
╰─────────────────────────────────────╯

╭─ Response ────────────────────────────────────────────────────────╮
│ I identified 3 potential security concerns in the auth module:    │
│                                                                   │
│ 1. Token expiry not validated in `validate_session()` at          │
│    src/auth/session.py:42. The JWT expiry claim is decoded but    │
│    not checked against current time.                              │
│                                                                   │
│ 2. Missing rate limiting on the `/auth/refresh` endpoint.         │
│    An attacker could brute-force refresh tokens.                  │
│                                                                   │
│ 3. Low severity: Password hashing uses bcrypt with cost=10.       │
│    Consider cost=12 for production deployments.                   │
╰───────────────────────────────────────────────────────────────────╯

╭─ Usage ──────────────╮
│ Tokens: 2,840        │
│ Duration: 4.2s       │
│ Cost: $0.009         │
│ Tool Calls: 6        │
╰──────────────────────╯

✓ OK Actor run completed
Saving actor output to a file:

$ agents actor run --output review.md local/reviewer "Write a code review report"

╭─ Actor Run ───────────────╮
│ Actor: local/reviewer     │
│ Output: review.md         │
│ Tokens: 4,120             │
│ Duration: 6.8s            │
╰───────────────────────────╯

✓ OK Output written to review.md (2.4 KB)
##### agents actor add **Purpose** Add a new actor configuration, or replace an existing one with `--update`. An actor can be defined via a YAML configuration file, entirely from CLI options, or a combination of both. When both a `--config` file and CLI options are provided, the CLI options act as overrides for values defined in the configuration file. If a local actor with the same name already exists, the command fails unless the `--update` flag is provided, which replaces the existing actor. **Arguments** - `[]`: Actor name (optional). When provided via a config file, the name can be omitted from the command line. - `--config/-c FILE`: Actor config file. When provided, values from the file serve as defaults. When omitted, the actor is defined entirely from CLI options. - `--type graph|agent`: Actor type. - `--skill NAME`: Skill to attach to the actor (repeatable). - `--description/-d TEXT`: Actor description. - `--unsafe`: Mark actor as unsafe. - `--option/-o key=value`: Option override (repeatable). - `--update`: Replace an existing local actor with the same name. Without this flag, attempting to add an actor whose name is already registered will fail. When `--config` is provided alongside CLI options, the CLI options override the corresponding values from the config file. **Examples**

$ agents actor add --config ./actors/reviewer.yaml local/reviewer

╭─ Actor Added ────────╮
│ Name: local/reviewer │
│ Provider: openai     │
│ Model: gpt-4         │
│ Default: yes         │
│ Unsafe: no           │
│ Type: graph          │
╰──────────────────────╯

╭─ Config ─────────────────────╮
│ Path: ./actors/reviewer.yaml │
│ Hash: 8b3f3d2                │
│ Options: 4                   │
│ Nodes: 3                     │
│ Edges: 4                     │
╰──────────────────────────────╯

╭─ Capabilities ───────╮
│ - code review        │
│ - diff summarization │
│ - lint guidance      │
╰──────────────────────╯

╭─ Tools ────────────────────────╮
│ Tool          Read-Only  Safe  │
│ ────────────  ─────────  ────  │
│ read_file     yes        yes   │
│ search_files  yes        yes   │
│ git_diff      yes        yes   │
╰────────────────────────────────╯

✓ OK Actor added
Attempting to register an actor that already exists (without `--update`):

$ agents actor add --config ./actors/reviewer.yaml

╭─ Error ─────────────────────────────────────────────────╮
│ Actor already exists: local/reviewer                    │
│ Registered: 2026-02-07 14:22                            │
│ Use --update to replace the existing actor definition.  │
╰─────────────────────────────────────────────────────────╯

✗ ERROR Actor already registered — use --update to replace
Updating an existing actor with `--update` and adding a skill:

$ agents actor add --config ./actors/reviewer.yaml --update --skill local/code-analysis

╭─ Actor Updated ─────────────────────╮
│ Name: local/reviewer                │
│ Type: agent                         │
│ Status: updated                     │
╰─────────────────────────────────────╯

╭─ Changes ────────────────────────────╮
│ Skills: +1 (local/code-analysis)     │
│ Model: unchanged                     │
│ Graph: updated                       │
╰──────────────────────────────────────╯

✓ OK Actor updated
##### agents actor remove **Purpose** Remove a custom actor. **Arguments** - ``: Actor name. **Examples**

$ agents actor remove local/reviewer

╭─ Actor Removed ──────╮
│ Name: local/reviewer │
│ Provider: openai     │
│ Model: gpt-4         │
╰──────────────────────╯

╭─ Impact ───────────────────────────────────╮
│ Sessions: 0 affected                       │
│ Active Plans: 0 affected                   │
│ Actions Referencing: 0                     │
╰────────────────────────────────────────────╯

╭─ Cleanup ──────────────╮
│ Config: kept on disk   │
│ Contexts: 1 orphaned   │
╰────────────────────────╯

✓ OK Actor removed
##### agents actor list **Purpose** List all actors. **Arguments** None. **Examples**

$ agents actor list

╭─ Actors ──────────────────────────────────────────────────────────────╮
│ Name            Provider   Model       Default  Built-in  Unsafe      │
│ ──────────────  ─────────  ──────────  ───────  ────────  ──────      │
│ local/reviewer  openai     gpt-4       ✓                  no          │
│ openai/gpt-4    openai     gpt-4                ✓         no          │
│ anthropic/3.5   anthropic  claude-3.5           ✓         no          │
╰───────────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────╮
│ Total: 3               │
│ Built-in: 2            │
│ Custom: 1              │
│ Unsafe: 0              │
│ Providers Used: 2      │
╰────────────────────────╯

✓ OK 3 actors listed
##### agents actor show **Purpose** Show details for a single actor. **Arguments** - ``: Actor name. **Examples**

$ agents actor show local/reviewer

╭─ Actor Details ────────────────────╮
│ Name: local/reviewer               │
│ Provider: openai                   │
│ Model: gpt-4                       │
│ Default: yes                       │
│ Built-in: no                       │
│ Unsafe: no                         │
│ Type: graph                        │
│ Created: 2026-02-08 12:35          │
│ Updated: 2026-02-08 12:40          │
│ Config: ./actors/reviewer.yaml     │
│ Config Hash: 9c4e2a1               │
╰────────────────────────────────────╯

╭─ Options ──────────╮
│ - temperature: 0.2 │
│ - max_tokens: 2048 │
│ - top_p: 1.0       │
╰────────────────────╯

╭─ Graph Structure ─╮
│ Nodes: 3          │
│ Edges: 4          │
│ Entry: analyze    │
│ Exit: report      │
╰───────────────────╯

╭─ Tools ────────────────────────╮
│ Tool          Read-Only  Safe  │
│ ────────────  ─────────  ────  │
│ read_file     yes        yes   │
│ search_files  yes        yes   │
│ git_diff      yes        yes   │
╰────────────────────────────────╯

╭─ Permissions ───────╮
│ Unsafe: no          │
│ Filesystem: allowed │
│ Network: restricted │
╰─────────────────────╯

╭─ Usage ─────────────────────────────────────────╮
│ Referenced by Actions: 1 (local/code-coverage)  │
│ Active in Sessions: 0                           │
│ Total Runs: 14                                  │
│ Avg Cost/Run: $0.0032                           │
╰─────────────────────────────────────────────────╯

✓ OK Actor loaded
##### agents actor context **Purpose** Manage manual context for actor runs. These commands mirror the legacy context behavior but are scoped to an actor context name. ###### agents actor context rm **Purpose** Remove files or directories from an actor context. **Arguments** - ``: Context name (positional argument). Use --all/-a to target all contexts. - `--all, -a`: Target all contexts instead of a named one. - `--yes, -y`: Skip confirmation prompt. **Examples**

$ agents actor context rm docs

╭─ Context Removed ───────────╮
│ Context: docs               │
│ Status: removed             │
╰─────────────────────────────╯

╭─ Stats ───────────────────╮
│ Remaining Size: 48 KB     │
│ Updated: 2026-02-08 13:06 │
╰───────────────────────────╯

✓ OK Context updated
###### agents actor context list **Purpose** List files stored in an actor context. **Arguments** - `[REGEX]`: Optional regex filter for context names. **Examples**

$ agents actor context list docs

╭─ Context Files ──────────────────────────────╮
│ Name              Type  Size     Added       │
│ ────────────────  ────  ───────  ──────────  │
│ README.md         file  4.2 KB   02-08 12:10 │
│ docs/overview.md  file  12.8 KB  02-08 12:10 │
│ docs/cli.md       file  9.5 KB   02-08 12:10 │
╰──────────────────────────────────────────────╯

╭─ Stats ───────────────────────╮
│ Total Files: 3                │
│ Total Size: 26.5 KB           │
│ Estimated Tokens: ~6,600      │
│ Languages: Markdown           │
╰───────────────────────────────╯

✓ OK 3 files listed
###### agents actor context show **Purpose** Show content of a file in an actor context. **Arguments** - ``: Context name (positional argument). **Examples**

$ agents actor context show docs

╭─ Context Summary ───────────╮
│ Context: docs               │
│ Files: 3                    │
│ Total Size: 26.5 KB         │
│ Estimated Tokens: ~6,600    │
│ Created: 2026-02-08 12:10   │
╰─────────────────────────────╯

✓ OK Context displayed
###### agents actor context export **Purpose** Export a context as JSON. **Arguments** - ``: Context name (positional argument). - `--output/-o FILE`: Output file path (required). **Examples**

$ agents actor context export --output /tmp/docs-context.json docs

╭─ Context Export ───────────────╮
│ Context: docs                  │
│ Output: /tmp/docs-context.json │
│ Items: 12                      │
│ Size: 48 KB                    │
╰────────────────────────────────╯

╭─ Integrity ──────────────────╮
│ Checksum: sha256:19b2...a7d0 │
│ Compressed: no               │
╰──────────────────────────────╯

✓ OK Export completed
###### agents actor context import **Purpose** Import a context from JSON. **Arguments** - `[NAME]`: Context name (optional, inferred from file if omitted). - `--input/-i FILE`: Input JSON file (required). - `--update`: Replace an existing context with the same name. **Examples**

$ agents actor context import --input /tmp/docs-context.json docs

╭─ Context Import ──────────────╮
│ Context: docs                 │
│ Input: /tmp/docs-context.json │
│ Items: 12                     │
╰───────────────────────────────╯

╭─ Merge ───────────╮
│ Strategy: replace │
│ Conflicts: 0      │
╰───────────────────╯

✓ OK Import completed
###### agents actor context delete **Purpose** Delete an entire context. **Arguments** - ``: Context name (positional argument). Use --all/-a to target all contexts. - `--all, -a`: Target all contexts instead of a named one. - `--yes, -y`: Skip confirmation. **Examples**

$ agents actor context delete docs

Delete context docs? [y/N]: y

╭─ Context Deleted ────╮
│ Context: docs        │
│ Items: 12 removed    │
│ Storage: 48 KB freed │
╰──────────────────────╯

╭─ Cleanup ───────╮
│ Backups: none   │
│ Logs: preserved │
╰─────────────────╯

✓ OK Context deleted
###### agents actor context clear **Purpose** Clear all files from a context but keep the context itself. **Arguments** - ``: Context name (positional argument). Use --all/-a to target all contexts. - `--all, -a`: Target all contexts instead of a named one. - `--yes, -y`: Skip confirmation. **Examples**

$ agents actor context clear docs

Clear context docs? [y/N]: y

╭─ Context Cleared ────╮
│ Context: docs        │
│ Items: 12 removed    │
│ Storage: 48 KB freed │
╰──────────────────────╯

╭─ Retention ────────╮
│ Context: preserved │
│ Files: removed     │
╰────────────────────╯

✓ OK Context cleared
#### agents skill **Purpose** Manage skills — reusable, namespaced collections of tools. Skills are defined in their own YAML configuration files and registered in the system through these commands. Once registered, skills can be referenced by actors. ##### agents skill add **Purpose** Register a new skill. A skill can be defined via a YAML configuration file, entirely from CLI options, or a combination of both. When both a `--config` file and CLI options are provided, the CLI options act as overrides for values defined in the configuration file. If a skill with the same name already exists, the command fails unless the `--update` flag is provided, which allows overwriting the existing registration with the new configuration. **Arguments** - `[]`: Namespaced skill name (optional). When provided via a config file, the name can be omitted from the command line. - `--config/-c FILE`: Path to the skill YAML configuration file. When provided, values from the file serve as defaults. When omitted, the skill is defined entirely from CLI options. - `--description/-d TEXT`: Optional description (overrides what's in the YAML when both are provided). - `--tool NAME`: Named tool to include in the skill (repeatable). References tools from the Tool Registry. - `--include-skill NAME`: Include another skill's tools (repeatable). References skills by fully-qualified name. - `--mcp-server spec`: MCP server specification to expose tools from (repeatable). - `--update`: Allow overwriting an existing skill registration. Without this flag, attempting to add a skill whose name is already registered will fail. When `--config` is provided alongside CLI options (`--tool`, `--include-skill`, `--mcp-server`, `--description`), the CLI options override or extend the corresponding values from the config file. **Examples** Registering a new skill:

$ agents skill add --config ./skills/devops-toolkit.yaml local/devops-toolkit

╭─ Skill Registered ────────────────────────╮
│ Name: local/devops-toolkit                │
│ ID: 01HXMC4T08Y0N5R9VZ4QX4BPTZ1           │
│ Description: Full-stack development tools │
│ Config: ./skills/devops-toolkit.yaml      │
│ Created: 2026-02-08 13:10                 │
╰───────────────────────────────────────────╯

╭─ Includes ────────────────────╮
│ local/file-ops (registered)   │
│ local/git-ops (registered)    │
│ local/github (registered)     │
╰───────────────────────────────╯

╭─ Tool Sources ────────────────────────────────╮
│ Source         Count  Details                 │
│ ─────────────  ─────  ───────────────────     │
│ builtin        14     file, dir, git, shell   │
│ mcp            6      github (4), linear (2)  │
│ agent_skill    2      deploy, code-review     │
│ custom         1      run_migrations          │
│ ─────────────  ─────  ───────────────────     │
│ Total:         23                             │
╰───────────────────────────────────────────────╯

╭─ MCP Servers ────────────────────╮
│ linear: validated (2 tools)      │
╰──────────────────────────────────╯

✓ OK Skill registered with 23 tools
Attempting to add a skill that already exists (without `--update`):

$ agents skill add --config ./skills/devops-toolkit-v2.yaml local/devops-toolkit

✗ Error: Skill 'local/devops-toolkit' is already registered.

To overwrite the existing configuration, re-run with --update:

  agents skill add --config ./skills/devops-toolkit-v2.yaml --update local/devops-toolkit
Updating an existing skill with `--update`:

$ agents skill add --config ./skills/devops-toolkit-v2.yaml --update local/devops-toolkit

╭─ Skill Updated ───────────────────────────╮
│ Name: local/devops-toolkit                │
│ Description: Full-stack development tools │
│ Updated: 2026-02-08 14:22                 │
╰───────────────────────────────────────────╯

╭─ Changes ─────────────────────╮
│ Tools Added: 2                │
│ Tools Removed: 0              │
│ Tools Modified: 1             │
│ Includes Changed: no          │
│ MCP Servers Changed: no       │
╰───────────────────────────────╯

╭─ Affected Actors ─────────────────────────────╮
│ Warning: 2 actors reference this skill:       │
│ - local/code-assistant                        │
│ - local/full-stack-assistant                  │
│ These actors will pick up changes on next use │
╰───────────────────────────────────────────────╯

✓ OK Skill updated (23 → 25 tools)
##### agents skill remove **Purpose** Remove a registered skill. **Arguments** - ``: Skill name to remove. - `--yes`: Skip confirmation prompt. **Examples**

$ agents skill remove local/devops-toolkit

Remove skill local/devops-toolkit? [y/N]: y

╭─ Skill Removed ──────────────────────╮
│ Name: local/devops-toolkit           │
│ Tools: 23 removed from registry      │
│ MCP Servers: 1 connection closed     │
╰──────────────────────────────────────╯

╭─ Dependency Check ─────────────────────────────────╮
│ Warning: 1 skill includes this skill:              │
│ - local/full-stack-dev (will lose devops tools)    │
│ Warning: 2 actors reference this skill:            │
│ - local/code-assistant                             │
│ - local/full-stack-assistant                       │
╰────────────────────────────────────────────────────╯

✓ OK Skill removed
##### agents skill list **Purpose** List registered skills with optional filters. **Arguments** - `--namespace/-n NS`: Filter by namespace. - `--source SOURCE`: Filter by tool source type (mcp, agent_skill, builtin, custom). **Examples**

$ agents skill list

╭─ Skills ────────────────────────────────────────────────────────────╮
│ Name                    Tools  Includes  Sources                    │
│ ──────────────────────  ─────  ────────  ──────────────────────     │
│ local/file-ops          9      0         builtin                    │
│ local/git-ops           4      0         builtin                    │
│ local/github            4      0         mcp                        │
│ local/devops-toolkit    23     3         builtin, mcp, custom       │
│ local/full-stack-dev    25     4         builtin, mcp, custom       │
╰─────────────────────────────────────────────────────────────────────╯

╭─ Summary ─────────╮
│ Total: 5          │
│ Local: 5          │
│ Server: 0         │
│ Total Tools: 28   │
╰───────────────────╯

✓ OK 5 skills listed
##### agents skill show **Purpose** Show full details for a registered skill, including its includes, tool sources, and capability summary. **Arguments** - ``: Skill name. **Examples**

$ agents skill show local/devops-toolkit

╭─ Skill Details ──────────────────────────────╮
│ Name: local/devops-toolkit                   │
│ ID: 01HXMC4T08Y0N5R9VZ4QX4BPTZ1              │
│ Description: Full-stack development tools    │
│ Config: ./skills/devops-toolkit.yaml         │
│ Created: 2026-02-08 13:10                    │
│ Updated: 2026-02-08 14:22                    │
╰──────────────────────────────────────────────╯

╭─ Includes (3) ────────────────────────────╮
│ local/file-ops  → 9 tools (builtin)       │
│ local/git-ops   → 4 tools (builtin)       │
│ local/github    → 4 tools (mcp)           │
╰───────────────────────────────────────────╯

╭─ Direct Tools (6) ────────────────────────────────────────╮
│ Name              Source       Writes  Checkpoint         │
│ ────────────────  ───────────  ──────  ──────────         │
│ create_issue      mcp:linear   yes     no                 │
│ list_issues       mcp:linear   no      —                  │
│ deploy-to-staging agent_skill  yes     composite          │
│ code-review       agent_skill  no      —                  │
│ run_migrations    custom       yes     transaction        │
│ shell_execute     builtin      yes     snapshot           │
╰───────────────────────────────────────────────────────────╯

╭─ MCP Servers (1) ───────────────────╮
│ linear: stdio, 2 tools, connected   │
╰─────────────────────────────────────╯

╭─ Capability Summary ──────────╮
│ Total Tools: 23               │
│ Read-Only: 10                 │
│ Writes: 13                    │
│ Checkpointable: 10            │
│ Has Side Effects: 3           │
│ Requires Approval: 1          │
╰───────────────────────────────╯

╭─ Referenced By ───────────────────╮
│ Actors: local/code-assistant      │
│ Skills: local/full-stack-dev      │
╰───────────────────────────────────╯

✓ OK Skill loaded
##### agents skill tools **Purpose** List all tools provided by a skill, including those inherited from included skills (the flattened tool set). **Arguments** - ``: Skill name. **Examples**

$ agents skill tools local/devops-toolkit

╭─ Tools for local/devops-toolkit ─────────────────────────────────────────────────────────╮
│ Tool               Source       From Skill      Read-Only  Writes  Checkpoint            │
│ ─────────────────  ───────────  ──────────────  ─────────  ──────  ──────────            │
│ read_file          builtin      local/file-ops  ✓          —       —                     │
│ write_file         builtin      local/file-ops  —          ✓       file                  │
│ edit_file          builtin      local/file-ops  —          ✓       file                  │
│ delete_file        builtin      local/file-ops  —          ✓       file                  │
│ move_file          builtin      local/file-ops  —          ✓       file                  │
│ copy_file          builtin      local/file-ops  —          ✓       file                  │
│ create_directory   builtin      local/file-ops  —          ✓       file                  │
│ list_directory     builtin      local/file-ops  ✓          —       —                     │
│ delete_directory   builtin      local/file-ops  —          ✓       file                  │
│ git_status         builtin      local/git-ops   ✓          —       —                     │
│ git_diff           builtin      local/git-ops   ✓          —       —                     │
│ git_log            builtin      local/git-ops   ✓          —       —                     │
│ git_blame          builtin      local/git-ops   ✓          —       —                     │
│ create_issue       mcp:github   local/github    —          ✓       no                    │
│ create_pr          mcp:github   local/github    —          ✓       no                    │
│ list_repos         mcp:github   local/github    ✓          —       —                     │
│ get_file_contents  mcp:github   local/github    ✓          —       —                     │
│ create_issue       mcp:linear   (direct)        —          ✓       no                    │
│ list_issues        mcp:linear   (direct)        ✓          —       —                     │
│ run_migrations     custom       (direct)        —          ✓       transaction           │
│ deploy-to-staging  agent_skill  (direct)        —          ✓       composite             │
│ code-review        agent_skill  (direct)        ✓          —       —                     │
│ shell_execute      builtin      (direct)        —          ✓       snapshot              │
╰──────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────────╮
│ Total: 23                  │
│ From Includes: 17          │
│ Direct: 6                  │
│ Read-Only: 10              │
│ Writes: 13                 │
│ Checkpointable: 10         │
╰────────────────────────────╯

✓ OK 23 tools listed
#### agents tool **Purpose** Manage tools — namespaced, independently registered, callable operations. Tools are defined in their own YAML configuration files and registered in the system through these commands. Once registered, tools can be referenced by name in skills (as part of a tool collection) and in actor graphs (as tool nodes). ##### agents tool add **Purpose** Register a new tool. A tool can be defined via a YAML configuration file, entirely from CLI options, or a combination of both. When both a `--config` file and CLI options are provided, the CLI options act as overrides for values defined in the configuration file. If a tool with the same name already exists, the command fails unless the `--update` flag is provided, which allows overwriting the existing registration with the new configuration. **Arguments** - ``: Namespaced tool name (appears at the end of the command). - `--config/-c FILE`: Path to the tool YAML configuration file. When provided, values from the file serve as defaults. When omitted, the tool is defined entirely from CLI options. - `--description/-d TEXT`: Optional description (overrides what's in the YAML when both are provided). - `--source SOURCE`: Tool source type (custom, mcp, agent_skill, builtin). - `--input-schema JSON`: JSON Schema for tool inputs. - `--code TEXT`: Inline Python code for custom tools. - `--writes/--no-writes`: Whether the tool performs write operations. - `--checkpointable/--no-checkpointable`: Whether the tool supports checkpointing. - `--update`: Allow overwriting an existing tool registration. Without this flag, attempting to add a tool whose name is already registered will fail. When `--config` is provided alongside CLI options, the CLI options override the corresponding values from the config file. **Examples** Registering a new tool:

$ agents tool add --config ./tools/run-migrations.yaml local/run-migrations

╭─ Tool Registered ────────────────────────────╮
│ Name: local/run-migrations                   │
│ ID: 01HXMC5T09Z1N6S0WZ5RY5CQUZ2              │
│ Description: Run database migrations         │
│ Source: custom                               │
│ Config: ./tools/run-migrations.yaml          │
│ Created: 2026-02-09 10:15                    │
╰──────────────────────────────────────────────╯

╭─ Capability ─────────────────────╮
│ Writes: true                     │
│ Write Scope: database:migrations │
│ Checkpointable: true             │
│ Checkpoint Scope: transaction    │
│ Side Effects: schema_mutation    │
╰──────────────────────────────────╯

✓ OK Tool registered
Attempting to add a tool that already exists (without `--update`):

$ agents tool add --config ./tools/run-migrations-v2.yaml local/run-migrations

✗ Error: Tool 'local/run-migrations' is already registered.

To overwrite the existing configuration, re-run with --update:

  agents tool add --config ./tools/run-migrations-v2.yaml --update local/run-migrations
Updating an existing tool with `--update`:

$ agents tool add --config ./tools/db-migrate.yaml --update

╭─ Tool Updated ─────────────────────────────╮
│ Name: local/db-migrate                     │
│ Source: custom (Python)                    │
│ Status: updated (was version 1 → now 2)    │
╰────────────────────────────────────────────╯

╭─ Changes ─────────────────────────────────────╮
│ Input Schema: modified (added batch_size)     │
│ Capabilities: unchanged (writes, checkpoint)  │
│ Description: updated                          │
╰───────────────────────────────────────────────╯

╭─ References ──────────────────╮
│ Skills: 1 (local/db-tools)    │
│ Actors: 0                     │
│ Referencing skills will use   │
│ the updated definition.       │
╰───────────────────────────────╯

✓ OK Tool updated
##### agents tool remove **Purpose** Remove a registered tool. **Arguments** - ``: Tool name to remove. - `--yes`: Skip confirmation prompt. **Examples**

$ agents tool remove local/run-migrations

Remove tool local/run-migrations? [y/N]: y

╭─ Tool Removed ────────────────────╮
│ Name: local/run-migrations        │
│ Source: custom                    │
╰───────────────────────────────────╯

╭─ References ──────────────────────────────────────╮
│ Warning: This tool is referenced by:              │
│ - Skill: local/devops-toolkit                     │
│ - Actor graph node: code_executor.run_db_migrate  │
│ These references will break until resolved.       │
╰───────────────────────────────────────────────────╯

✓ OK Tool removed
##### agents tool list **Purpose** List registered tools with optional filters. **Arguments** - `--namespace/-n NS`: Filter by namespace. - `--source SOURCE`: Filter by tool source type (mcp, agent_skill, builtin, custom). **Examples**

$ agents tool list --namespace local

╭─ Tools ──────────────────────────────────────────────────╮
│ Name                       Source   Read-Only  Writes    │
│ ─────────────────────────  ───────  ─────────  ──────    │
│ local/run-migrations       custom   —          ✓         │
│ local/validate-api-compat  custom   —          ✓         │
│ local/create-subplan       custom   —          ✓         │
│ local/deploy-staging       agent    —          ✓         │
╰──────────────────────────────────────────────────────────╯

╭─ Summary ────────╮
│ Total: 4         │
│ Read-Only: 0     │
│ Writes: 4        │
╰──────────────────╯

✓ OK 4 tools listed
##### agents tool show **Purpose** Show full details for a registered tool, including its schema, capability metadata, and where it is referenced. **Arguments** - ``: Tool name. **Examples**

$ agents tool show local/run-migrations

╭─ Tool Details ───────────────────────────────╮
│ Name: local/run-migrations                   │
│ Description: Run database migrations         │
│ Source: custom                               │
│ Config: ./tools/run-migrations.yaml          │
│ Registered: 2026-02-09 10:15                 │
╰──────────────────────────────────────────────╯

╭─ Input Schema ─────────────────────────────────╮
│ direction: string (required) enum: [up, down]  │
│ count: integer (default: 1)                    │
╰────────────────────────────────────────────────╯

╭─ Capability ──────────────────────╮
│ Read-Only: false                  │
│ Writes: true                      │
│ Write Scope: database:migrations  │
│ Checkpointable: true              │
│ Checkpoint Scope: transaction     │
│ Side Effects: schema_mutation     │
│ Idempotent: false                 │
╰───────────────────────────────────╯

╭─ Referenced By ───────────────────────╮
│ Skills:                               │
│   - local/devops-toolkit              │
│ Actor Graph Nodes:                    │
│   - code_executor.run_db_migrate      │
╰───────────────────────────────────────╯

✓ OK Tool details loaded
#### agents resource type **Purpose** Manage resource type definitions. Resource types are schema-level definitions that constrain categories of resources — they define what CLI arguments `agents resource add ` accepts, whether instances are physical or virtual, allowed parent/child types, auto-discovery behavior, sandbox strategy, and handler implementation. Built-in types (`git-checkout`, `git`, `fs-mount`, `fs-directory`, and their child types) are hardcoded. Custom types are defined in YAML configuration files and extend the system with new `agents resource add` subcommands. ##### agents resource type add **Purpose** Register a new custom resource type. A resource type can be defined via a YAML configuration file, entirely from CLI options, or a combination of both. When both a `--config` file and CLI options are provided, the CLI options act as overrides for values defined in the configuration file. **Arguments** - `[]`: Namespaced resource type name (e.g., `local/svn`, `cleverthis/s3-bucket`). Optional when provided via a config file. - `--config/-c FILE`: Path to the resource type YAML configuration file. When provided, values from the file serve as defaults. When omitted, the resource type is defined entirely from CLI options. - `--physical/--virtual`: Whether instances of this type are physical or virtual. - `--user-addable/--no-user-addable`: Whether instances can be created directly by users. - `--sandbox-strategy NAME`: Sandbox strategy for instances of this type (e.g., `copy_on_write`, `transaction_rollback`, `none`). - `--handler NAME`: Resource handler implementation. - `--child-type spec`: Allowed child resource type specification (repeatable). - `--cli-arg spec`: CLI argument definition for `agents resource add ` (repeatable). - `--update`: If the type name already exists, overwrite it. Without this flag, adding a duplicate name fails with an error. When `--config` is provided alongside CLI options, the CLI options override the corresponding values from the config file. **Examples**

$ agents resource type add --config ./resource-types/svn.yaml local/svn

╭─ Resource Type ────────────────────╮
│ Name: local/svn                    │
│ Physical/Virtual: physical         │
│ User Addable: yes                  │
│ Registered: 2026-02-09 10:15       │
╰────────────────────────────────────╯

╭─ CLI Arguments ────────────────────────╮
│ Argument      Required  Description    │
│ ────────────  ────────  ─────────────  │
│ --url         yes       Repository URL │
│ --checkout    no        Local checkout │
╰────────────────────────────────────────╯

╭─ Child Types ──────────────────────────╮
│ Auto-discover: svn-revision, svn-file  │
│ Manual link: fs-mount                  │
╰────────────────────────────────────────╯

╭─ Sandbox ──────────────────────╮
│ Strategy: copy_on_write        │
│ Handler: SVNHandler (custom)   │
╰────────────────────────────────╯

✓ OK Resource type registered
 New subcommand available: agents resource add local/svn
##### agents resource type remove **Purpose** Remove a custom resource type. Built-in types cannot be removed. Fails if any registered resources use this type. **Arguments** - ``: Resource type name. - `--yes`: Skip confirmation prompt. **Examples**

$ agents resource type remove local/svn

Remove resource type local/svn? [y/N]: y

╭─ Resource Type Removed ───────╮
│ Name: local/svn               │
│ Resources Using: 0            │
│ Subcommand Removed: yes       │
╰───────────────────────────────╯

✓ OK Resource type removed
##### agents resource type list **Purpose** List all registered resource types (built-in and custom). **Arguments** None (use the global `--format` option to control output format). **Examples**

$ agents resource type list

╭─ Resource Types ──────────────────────────────────────────────────────────────────────────────────╮
│ Name                 Source    Phys/Virt  Addable  Auto-children                                  │
│ ───────────────────  ────────  ─────────  ───────  ──────────────────────────────────────         │
│ git-checkout        built-in  physical   yes      git, fs-directory                               │
│ git                 built-in  physical   yes      git-remote, git-branch, git-tag,                │
│                                                    git-commit, git-stash, git-submodule           │
│ git-remote          built-in  physical   no       (none)                                          │
│ git-branch          built-in  physical   no       git-commit                                      │
│ git-tag             built-in  physical   no       (none)                                          │
│ git-commit          built-in  physical   no       git-tree                                        │
│ git-tree            built-in  physical   no       git-tree-entry                                  │
│ git-tree-entry      built-in  physical   no       (none)                                          │
│ git-stash           built-in  physical   no       (none)                                          │
│ git-submodule       built-in  physical   no       (none)                                          │
│ fs-mount            built-in  physical   yes      fs-directory                                    │
│ fs-directory        built-in  physical   yes      fs-file, fs-directory,                          │
│                                                    fs-symlink, fs-hardlink                        │
│ fs-file             built-in  physical   no       (none)                                          │
│ fs-symlink          built-in  physical   no       (none)                                          │
│ fs-hardlink         built-in  physical   no       (none)                                          │
│ file                built-in  virtual    no       (none)                                          │
│ directory           built-in  virtual    no       (none)                                          │
│ symlink             built-in  virtual    no       (none)                                          │
│ commit              built-in  virtual    no       (none)                                          │
│ branch              built-in  virtual    no       (none)                                          │
│ tag                 built-in  virtual    no       (none)                                          │
│ remote              built-in  virtual    no       (none)                                          │
│ submodule           built-in  virtual    no       (none)                                          │
│ tree                built-in  virtual    no       (none)                                          │
│ local/svn           custom    physical   yes      svn-revision, svn-file                          │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ─────────────────╮
│ Built-in: 24              │
│ Custom: 1                 │
│ User Addable: 4           │
╰───────────────────────────╯

✓ OK 25 resource types listed
##### agents resource type show **Purpose** Show detailed information about a resource type, including its full schema. **Arguments** - ``: Resource type name. **Examples**

$ agents resource type show git-checkout

╭─ Resource Type ──────────────────────────────────────────────────────╮
│ Name: git-checkout                                                   │
│ Source: built-in                                                     │
│ Description: A locally checked-out git repository with worktree      │
│ Physical/Virtual: physical                                           │
│ User Addable: yes                                                    │
╰──────────────────────────────────────────────────────────────────────╯

╭─ CLI Arguments (agents resource add git-checkout) ─────────────────╮
│ Argument    Required  Type    Description                          │
│ ──────────  ────────  ──────  ──────────────────────────────       │
│ --path      yes       path    Local checkout directory             │
│ --branch    no        string  Default branch (default: main)       │
╰────────────────────────────────────────────────────────────────────╯

╭─ Parent Types ───────────────╮
│ Allowed: (any, top-level OK) │
╰──────────────────────────────╯

╭─ Child Types ────────────────────────────────────────────────────╮
│ Type           Auto  Manual Link  Description                    │
│ ─────────────  ────  ───────────  ──────────────────────────     │
│ git            yes   no           Repo object DB and history     │
│ fs-directory   yes   no           Worktree root directory        │
╰──────────────────────────────────────────────────────────────────╯

╭─ Sandbox ──────────────────────╮
│ Strategy: git_worktree         │
│ Checkpointable: yes            │
│ Handler: GitCheckoutHandler    │
╰────────────────────────────────╯

✓ OK Resource type details loaded
#### agents resource **Purpose** Manage independently registered resources. Resources represent anything that can be read, written, or queried — git repositories, filesystems, databases, APIs, and more. Resources are registered independently of projects and linked to one or more projects via `agents project link-resource`. Resources form a directed acyclic graph (DAG) with parent/child relationships, and child resources are often auto-discovered when a parent resource is registered. ##### agents resource add **Purpose** Register a new resource. The `add` command uses type-specific subcommands — each registered resource type (built-in or custom) provides its own subcommand with type-appropriate arguments. **Arguments** - `--description/-d TEXT`: Optional resource description. - `--update`: If the resource name already exists, replace it. Without this flag, adding a duplicate name fails with an error. - ``: Resource type name (e.g., `git-checkout`, `git`, `fs-mount`, `fs-directory`, `local/svn`). Determines type-specific flags. - ``: Namespaced resource name (e.g., `local/api-repo`, `cleverthis/staging-db`). Type-specific flags depend on the resource type. See `agents resource type show ` for available arguments. **Examples**

$ agents resource add git-checkout local/api-repo --path /home/user/projects/api-service --branch main

╭─ Resource ─────────────────────────────╮
│ Name: local/api-repo                   │
│ Type: git-checkout                     │
│ Physical/Virtual: physical             │
│ Path: /home/user/projects/api-service  │
│ Branch: main                           │
│ Created: 2026-02-09 10:20              │
╰────────────────────────────────────────╯

╭─ Auto-discovered Children ───────────────────────────────────────╮
│ Name                         Type            Status              │
│ ───────────────────────────  ──────────────  ─────────────────   │
│ local/api-repo:repo        git             created               │
│ local/api-repo:repo:origin git-remote      created               │
│ local/api-repo:repo:main   git-branch      created               │
│ local/api-repo:repo:dev    git-branch      created               │
│ local/api-repo:worktree    fs-directory    created               │
│   + 47 git-commit resources                                      │
│   + 312 git-tree-entry resources                                 │
│   + 3 fs-directory + 28 fs-file                                  │
╰──────────────────────────────────────────────────────────────────╯

╭─ Capabilities ─────────────────╮
│ Readable: yes                  │
│ Writable: yes                  │
│ Sandboxable: yes               │
│ Checkpointable: yes            │
│ Sandbox Strategy: git_worktree │
╰────────────────────────────────╯

✓ OK Resource registered (395 child resources discovered)

$ agents resource add fs-mount local/docs --mount-path /docs/api-reference

╭─ Resource ───────────────────────────╮
│ Name: local/docs                     │
│ Type: fs-mount                       │
│ Physical/Virtual: physical           │
│ Mount Path: /docs/api-reference      │
│ Created: 2026-02-09 10:22            │
╰──────────────────────────────────────╯

╭─ Auto-discovered Children ────────────────────╮
│   + 1 fs-directory (root)                     │
│   + 3 fs-directory resources                  │
│   + 28 fs-file resources                      │
╰───────────────────────────────────────────────╯

╭─ Capabilities ──────────────────────╮
│ Readable: yes                       │
│ Writable: yes                       │
│ Sandboxable: yes                    │
│ Checkpointable: yes                 │
│ Sandbox Strategy: copy_on_write     │
╰─────────────────────────────────────╯

✓ OK Resource registered (31 child resources discovered)
**Duplicate name error:**

$ agents resource add git-checkout local/api-repo --path /repos/api-service

✗ Error: Resource local/api-repo already exists.
  Use --update to replace: agents resource add --update git-checkout local/api-repo --path /repos/api-service
##### agents resource remove **Purpose** Remove a registered resource and all its auto-discovered child resources. Fails if the resource is linked to any project (unlink first). **Arguments** - ``: Resource name. - `--yes`: Skip confirmation prompt. **Examples**

$ agents resource remove local/api-repo

Remove resource local/api-repo and 395 child resources? [y/N]: y

╭─ Resource Removed ──────────────────╮
│ Name: local/api-repo                │
│ Type: git-checkout                  │
│ Children Removed: 395               │
│ Projects Unlinked: 0                │
╰─────────────────────────────────────╯

✓ OK Resource removed
##### agents resource list **Purpose** List registered resources with optional filters. **Arguments** - `--namespace/-n NS`: Filter by namespace. - `--type/-t TYPE`: Filter by resource type. **Examples**

$ agents resource list

╭─ Resources ──────────────────────────────────────────────────────────────────╮
│ Name                Type        Phys/Virt  Children  Projects                │
│ ──────────────────  ──────────  ─────────  ────────  ────────────────        │
│ local/api-repo    git-checkout physical   395       local/api-service        │
│ local/docs        fs-mount     physical   32        local/api-service        │
│ local/staging-db  database    physical   12        local/api-service,        │
│                                                     local/staging            │
╰──────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ───────────╮
│ Total: 3            │
│ Physical: 3         │
│ Virtual: 0          │
│ Total Children: 405 │
╰─────────────────────╯

✓ OK 3 resources listed
##### agents resource show **Purpose** Show detailed information about a registered resource including its type, capabilities, parent/child relationships, and linked projects. **Arguments** - ``: Resource name. **Examples**

$ agents resource show local/api-repo

╭─ Resource ───────────────────────────────╮
│ Name: local/api-repo                     │
│ Type: git-checkout                       │
│ Physical/Virtual: physical               │
│ Path: /home/user/projects/api-service    │
│ Branch: main                             │
│ Created: 2026-02-09 10:20                │
╰──────────────────────────────────────────╯

╭─ Capabilities ────────────────────╮
│ Readable: yes                     │
│ Writable: yes                     │
│ Sandboxable: yes                  │
│ Checkpointable: yes               │
│ Sandbox Strategy: git_worktree    │
╰───────────────────────────────────╯

╭─ Parents ────╮
│ (top-level)  │
╰──────────────╯

╭─ Direct Children ────────────────────────────────────────────╮
│ Name                       Type          Auto  Children      │
│ ─────────────────────────  ────────────  ────  ────────      │
│ local/api-repo:repo      git           yes   363             │
│ local/api-repo:worktree  fs-directory  yes   32              │
╰──────────────────────────────────────────────────────────────╯

╭─ Linked Projects ─────────────╮
│ - local/api-service (r/w)     │
╰───────────────────────────────╯

╭─ Tool Bindings ────────────────────────────────╮
│ Tool                      Slot   Access        │
│ ────────────────────────  ─────  ───────────   │
│ (builtin) git_status     repo   read_only      │
│ (builtin) git_diff       repo   read_only      │
│ (builtin) git_log        repo   read_only      │
│ local/run-migrations     db     (not bound)    │
╰────────────────────────────────────────────────╯

✓ OK Resource details loaded
##### agents resource tree **Purpose** Display the resource DAG as a tree starting from a given resource, showing parent/child relationships. **Arguments** - ``: Root resource name. - `--depth/-d N`: Maximum depth to display (default: 3). - `--type/-t TYPE`: Filter to only show children of a specific type. **Examples**

$ agents resource tree local/api-repo --depth 2

╭─ Resource Tree: local/api-repo ─────────────────────────────────╮
│                                                                 │
│ local/api-repo (git-checkout, physical)                         │
│ ├── local/api-repo:repo (git, physical)                         │
│ │   ├── local/api-repo:repo:origin (git-remote)                 │
│ │   ├── local/api-repo:repo:main (git-branch)                   │
│ │   │   ├── ...repo:main:a1b2c3d (git-commit)                   │
│ │   │   └── ... 22 more git-commit resources                    │
│ │   └── local/api-repo:repo:dev (git-branch)                    │
│ │       └── ... 26 git-commit resources                         │
│ └── local/api-repo:worktree (fs-directory, physical)            │
│     ├── ...worktree:src/ (fs-directory)                         │
│     ├── ...worktree:tests/ (fs-directory)                       │
│     └── ... 28 more fs-file resources                           │
│                                                                 │
╰─────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────╮
│ Total shown: 14        │
│ Total in subtree: 395  │
│ Max depth: 2           │
╰────────────────────────╯

✓ OK Resource tree displayed
##### agents resource link-child **Purpose** Manually link one resource as a child of another. The child resource must already be registered. The resource types must be compatible (the parent type must allow the child type). A resource can have multiple parents. **Arguments** - `--parent/-p RESOURCE`: Parent resource name. - `--child/-c RESOURCE`: Child resource name. **Examples**

$ agents resource link-child --parent local/api-repo --child local/docs

╭─ Child Linked ────────────────────────╮
│ Parent: local/api-repo                │
│ Child: local/docs                     │
│ Child Type: fs-mount                  │
│ Status: linked                        │
╰───────────────────────────────────────╯

✓ OK Child resource linked
##### agents resource unlink-child **Purpose** Remove a manual parent/child link between two resources. Auto-discovered links cannot be manually unlinked. **Arguments** - `--parent/-p RESOURCE`: Parent resource name. - `--child/-c RESOURCE`: Child resource name. - `--yes`: Skip confirmation prompt. **Examples**

$ agents resource unlink-child --parent local/api-repo --child local/docs

Unlink local/docs from parent local/api-repo? [y/N]: y

╭─ Child Unlinked ──────────────────────╮
│ Parent: local/api-repo                │
│ Child: local/docs                     │
│ Status: unlinked                      │
╰───────────────────────────────────────╯

✓ OK Child resource unlinked
#### agents plan **Purpose** Manage plans through the Action -> Strategize -> Execute -> Apply lifecycle. ##### agents plan list **Purpose** List plans with optional filtering. **Arguments** - `--phase PHASE`: Filter by phase. - `--state STATE`: Filter by processing state. - `--project PROJECT`: Filter by project. - `--action ACTION`: Filter by action name. **Examples**

$ agents --format table plan list --phase execute

╭─ Plans ──────────────────────────────────────────────────────────────────────────────╮
│ ID        Phase    State       Action               Project            Elapsed       │
│ ────────  ───────  ──────────  ───────────────────  ─────────────────  ─────────     │
│ 01HXM7A9  execute  processing  local/code-coverage  local/api-service  00:01:12      │
╰──────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ──────╮
│ Phase: execute │
│ State: (any)   │
│ Project: (any) │
│ Action: (any)  │
╰────────────────╯

╭─ Summary ─────────╮
│ Total: 1          │
│ Processing: 1     │
│ Completed: 0      │
│ Errored: 0        │
╰───────────────────╯

✓ OK 1 plan listed
Listing all plans without filters shows plans across all phases and states:

$ agents plan list

╭─ Plans ──────────────────────────────────────────────────────────────────────────────────────╮
│ ID        Phase       State       Action               Project            Elapsed            │
│ ────────  ──────────  ──────────  ───────────────────  ─────────────────  ─────────          │
│ 01HXM7A9  execute     processing  local/code-coverage  local/api-service  00:01:12           │
│ 01HXM6R3  applied     complete    local/add-auth       local/api-service  00:07:14           │
│ 01HXM5K2  execute     errored     local/migrate-db     local/api-service  00:04:33           │
│ 01HXM4J1  strategize  processing  local/refactor-api   local/web-app      00:00:45           │
│ 01HXM3H8  cancelled   cancelled   local/add-logging    local/api-service  00:02:10           │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ─────────────╮
│ Total: 5              │
│ Processing: 2         │
│ Completed: 1          │
│ Errored: 1            │
│ Cancelled: 1          │
╰───────────────────────╯

✓ OK 5 plans listed
Filtering by project with `--project`:

$ agents plan list --project local/web-app

╭─ Plans ──────────────────────────────────────────────────────────────────────────────╮
│ ID        Phase       State       Action               Project        Elapsed        │
│ ────────  ──────────  ──────────  ───────────────────  ─────────────  ─────────      │
│ 01HXM4J1  strategize  processing  local/refactor-api   local/web-app  00:00:45       │
╰──────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ─────────────────╮
│ Phase: (any)              │
│ State: (any)              │
│ Project: local/web-app    │
│ Action: (any)             │
╰───────────────────────────╯

✓ OK 1 plan listed
##### agents plan use **Purpose** Apply an action to one or more projects and start the Strategize phase. **Arguments** - ``: Action name. - ``: One or more project names (positional arguments, repeatable). - `--arg/-a name=value`: Action argument (repeatable). - `--automation-profile PROFILE`: Automation profile name (e.g., `trusted`, `autonomous`, `local/careful-auto`). Overrides the profile inherited from the action, project, or global config. - `--strategy-actor ACTOR`: Override the action's strategy actor for this plan. - `--execution-actor ACTOR`: Override the action's execution actor for this plan. - `--estimation-actor ACTOR`: Override the action's estimation actor for this plan. - `--invariant-actor ACTOR`: Override the action's Invariant Reconciliation Actor for this plan. - `--invariant TEXT`: Invariant to attach to the created plan (repeatable). These are added as plan-level invariants in addition to any invariants inherited from the action, project, or global scope. All actor arguments (`--strategy-actor`, `--execution-actor`, `--estimation-actor`, `--invariant-actor`) are optional overrides. When provided, they replace whatever was set when creating the action. When omitted, the action's configured actors are used. **Examples**

$ agents plan use local/code-coverage local/api-service \
  --arg target_coverage_percent=85 --automation-profile trusted

╭─ Plan Created ──────────────────────╮
│ Plan ID: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Phase: strategize                   │
│ Action: local/code-coverage         │
│ Project: local/api-service          │
│ Automation: review                  │
│ Attempt: 1                          │
╰─────────────────────────────────────╯

╭─ Inputs ──────────────────────╮
│ - target_coverage_percent=85  │
│ - automation_profile=trusted  │
╰───────────────────────────────╯

╭─ Actors ────────────────────────╮
│ Strategy: local/strategist      │
│ Execution: local/executor       │
│ Estimation: (none)              │
╰─────────────────────────────────╯

╭─ Automation ─────────────────────────╮
│ Profile: supervised                  │
│ Source: project default              │
│ Read-Only: no                        │
╰──────────────────────────────────────╯

╭─ Context ───────────────────────╮
│ Resources: 2 (repo, db)         │
│ Indexed Files: 347              │
│ View: strategize                │
│ Hot Token Budget: 12,000        │
╰─────────────────────────────────╯

╭─ Next Steps ─────────────────────────────────────╮
│ - agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ - agents plan status 01HXM8C2ZK4Q7C2B3F2R4VYV6J  │
│ - agents plan tree 01HXM8C2ZK4Q7C2B3F2R4VYV6J    │
╰──────────────────────────────────────────────────╯

✓ OK Plan created
Applying an action to multiple projects simultaneously, with plan-level invariants:

$ agents plan use local/security-audit local/api-service local/web-app \
  --invariant "Never modify production database schemas" \
  --invariant "All changes must include test coverage" \
  --automation-profile supervised

╭─ Plan Created ──────────────────────────────────────╮
│ Plan: 01HXM9D2ZK4Q7C2B3F2R4VYV6J                    │
│ Action: local/security-audit                        │
│ Phase: strategize (running)                         │
│ Automation: supervised                              │
╰─────────────────────────────────────────────────────╯

╭─ Target Projects ───────────────────╮
│ 1. local/api-service (3 resources)  │
│ 2. local/web-app (2 resources)      │
╰─────────────────────────────────────╯

╭─ Plan Invariants ──────────────────────────────────────────╮
│ Scope     Source   Invariant                               │
│ ────────  ───────  ──────────────────────────────────      │
│ plan      CLI      Never modify production DB schemas      │
│ plan      CLI      All changes must include test coverage  │
│ project   config   API responses must be backward-compat   │
│ global    config   Follow Python PEP 8 style guide         │
╰────────────────────────────────────────────────────────────╯

✓ OK Plan created — strategize in progress
Using an action with custom actor overrides:

$ agents plan use local/code-coverage local/api-service \
  --strategy-actor local/senior-planner \
  --execution-actor local/fast-executor \
  --arg target_coverage_percent=95

╭─ Plan Created ──────────────────────────────────────╮
│ Plan: 01HXM9E3ZK4Q7C2B3F2R4VYV6J                    │
│ Action: local/code-coverage                         │
│ Phase: strategize (running)                         │
│ Automation: trusted                                 │
╰─────────────────────────────────────────────────────╯

╭─ Actor Overrides ────────────────────╮
│ Strategy: local/senior-planner       │
│ Execution: local/fast-executor       │
│ (Estimation: action default)         │
╰──────────────────────────────────────╯

╭─ Arguments ─────────────────╮
│ target_coverage_percent: 95 │
╰─────────────────────────────╯

✓ OK Plan created — strategize in progress
##### agents plan execute **Purpose** Start or resume execution for a plan. **Arguments** - ``: Plan ID (required). **Examples**

$ agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Execution ──────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Phase: execute                   │
│ Sandbox: git_worktree            │
│ Worker: local/executor           │
│ Started: 12:58:10                │
│ Attempt: 1                       │
╰──────────────────────────────────╯

╭─ Sandbox ──────────────────────────────────╮
│ Strategy: git_worktree                     │
│ Path: /repos/api/.worktrees/plan-01HXM8    │
│ Branch: cleveragents/plan-01HXM8C2         │
│ Status: active                             │
╰────────────────────────────────────────────╯

╭─ Strategy Summary ─────────────────────╮
│ Decisions: 8                           │
│ Invariants: 2                          │
│ Planned Child Plans: 2+                │
│ Estimated Files: ~12                   │
│ Risk: low                              │
╰────────────────────────────────────────╯

╭─ Progress ────────╮
│  Collect context │
│  Run tools       │
│  Build changeset │
│  Validate        │
╰───────────────────╯

✓ OK Execution started
Resuming execution of a plan that was previously paused or errored:

$ agents plan execute 01HXM7K2ZK4Q7C2B3F2R4VYV6J

╭─ Execution Resumed ─────────────────╮
│ Plan: 01HXM7K2ZK4Q7C2B3F2R4VYV6J    │
│ Phase: execute (resumed)            │
│ Sandbox: git_worktree               │
│ Worker: local/executor              │
│ Checkpoint: cp_01HXM8C2 (loaded)    │
│ Resumed From: step 4 of 6           │
╰─────────────────────────────────────╯

╭─ Previous Progress ──────────────────────╮
│  Step 1: Collect context (0.8s)         │
│  Step 2: Analyze codebase (4.2s)        │
│  Step 3: Generate migrations (6.1s)     │
│  Step 4: Apply migrations (errored)     │
│  Step 5: Update models (pending)        │
│  Step 6: Run validations (pending)      │
╰──────────────────────────────────────────╯

╭─ Guidance Applied ──────────────────────────────────────────────╮
│ "Use smaller batch sizes for the migration to avoid timeouts"   │
╰─────────────────────────────────────────────────────────────────╯

✓ OK Execution resumed from checkpoint cp_01HXM8C2
##### agents plan apply **Purpose** Apply sandboxed changes to real resources. **Arguments** - ``: Plan ID. - `--yes`: Skip confirmation. **Examples**

$ agents plan apply 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Apply changes for plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J? [y/N]: y

╭─ Apply Summary ─────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J    │
│ Artifacts: 6 files updated          │
│ Changes: 42 insertions, 9 deletions │
│ Project: local/api-service          │
│ Applied At: 2026-02-08 13:04        │
╰─────────────────────────────────────╯

╭─ Validation ───────────────────╮
│ Tests: passed (24/24)          │
│ Lint: passed (0 warnings)      │
│ Type Check: passed (0 errors)  │
│ Duration: 12.4s                │
╰────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮
│ Worktree: removed         │
│ Branch: merged to main    │
│ Checkpoint: archived      │
╰───────────────────────────╯

╭─ Plan Lifecycle ────────────────────────╮
│ Phase: applied                          │
│ State: complete                         │
│ Total Duration: 00:06:14                │
│ Total Cost: $0.0847                     │
│ Decisions Made: 8                       │
│ Child Plans: 2 (completed)              │
╰─────────────────────────────────────────╯

╭─ Next Steps ──────╮
│ - Review git diff │
│ - Commit changes  │
╰───────────────────╯

✓ OK Changes applied
When validations fail during apply, the changes are not committed and the plan enters a recoverable error state:

$ agents plan apply --yes 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Apply Summary ─────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J    │
│ Artifacts: 6 files updated          │
│ Changes: 42 insertions, 9 deletions │
│ Project: local/api-service          │
╰─────────────────────────────────────╯

╭─ Validation ───────────────────────────────────────────────╮
│  Tests: FAILED (22/24 passed, 2 failed)                   │
│   FAIL test_auth.py::test_session_refresh — AssertionError │
│   FAIL test_auth.py::test_token_expiry — TimeoutError      │
│  Lint: passed (0 warnings)                                │
│  Type Check: passed (0 errors)                            │
│ Duration: 14.8s                                            │
╰────────────────────────────────────────────────────────────╯

╭─ Sandbox Status ─────────────────────────────────────────────╮
│ Worktree: preserved (changes NOT committed)                  │
│ Checkpoint: cp_01HXM8C2 (pre-apply state available)          │
│ The sandbox is preserved for correction or manual review.    │
╰──────────────────────────────────────────────────────────────╯

╭─ Recovery Options ──────────────────────────────────────────────────╮
│ - agents plan prompt — provide guidance to fix test failures        │
│ - agents plan correct — revert and re-execute with guidance         │
│ - agents plan rollback — restore to a previous checkpoint           │
│ - agents plan cancel — abort the plan entirely                      │
╰─────────────────────────────────────────────────────────────────────╯

✗ ERROR Apply failed — 2 required validations did not pass
##### agents plan status **Purpose** Show detailed status for a plan. **Arguments** - ``: Plan ID (required). **Examples**

$ agents plan status 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Plan Status ────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Phase: execute                   │
│ State: processing                │
│ Action: local/code-coverage      │
│ Project: local/api-service       │
│ Automation: review               │
│ Attempt: 1                       │
╰──────────────────────────────────╯

╭─ Progress ───────╮
│  Strategize     │
│  Execute        │
│  Apply (queued) │
╰──────────────────╯

╭─ Timing ──────────╮
│ Started: 12:57:01 │
│ Elapsed: 00:01:12 │
│ ETA: 00:03:45     │
╰───────────────────╯

╭─ Execution Detail ──────────╮
│ Sandbox: git_worktree       │
│ Tool Calls: 8               │
│ Files Modified: 3           │
│ Child Plans: 1/2 complete   │
│ Checkpoints: 2 created      │
╰─────────────────────────────╯

╭─ Cost ───────────────╮
│ Tokens Used: 12,420  │
│ Cost So Far: $0.041  │
│ Estimated: $0.085    │
╰──────────────────────╯

✓ OK Status refreshed
Status of a plan that has completed successfully:

$ agents plan status 01HXM6R3ZK4Q7C2B3F2R4VYV6J

╭─ Plan Status ─────────────────────╮
│ Plan: 01HXM6R3ZK4Q7C2B3F2R4VYV6J  │
│ Phase: applied                    │
│ State: complete                   │
│ Action: local/add-auth            │
│ Project: local/api-service        │
│ Automation: trusted               │
│ Attempt: 1                        │
╰───────────────────────────────────╯

╭─ Progress ───────────╮
│  Strategize         │
│  Execute            │
│  Apply (committed)  │
╰──────────────────────╯

╭─ Timing ─────────────────────────╮
│ Started: 2026-02-08 12:57:01     │
│ Finished: 2026-02-08 13:04:15    │
│ Total Duration: 00:07:14         │
╰──────────────────────────────────╯

╭─ Result ────────────────────────────╮
│ Decisions Made: 8                   │
│ Child Plans: 2/2 complete           │
│ Artifacts: 6 files updated          │
│ Validations: 3/3 passed             │
│ Total Cost: $0.085                  │
╰─────────────────────────────────────╯

✓ OK Plan completed successfully
Status of a plan in the strategize phase:

$ agents plan status 01HXM9F2ZK4Q7C2B3F2R4VYV6J

╭─ Plan Status ────────────────────╮
│ Plan: 01HXM9F2ZK4Q7C2B3F2R4VYV6J │
│ Phase: strategize                │
│ State: processing                │
│ Action: local/refactor-auth      │
│ Project: local/api-service       │
│ Automation: supervised           │
╰──────────────────────────────────╯

╭─ Progress ───────────────╮
│  Strategize (running)   │
│  Execute (waiting)      │
│  Apply (waiting)        │
╰──────────────────────────╯

╭─ Strategy Progress ────────╮
│ Decisions Made: 4          │
│ Invariants Enforced: 2     │
│ Child Plans Planned: 3     │
│ Elapsed: 00:00:28          │
╰────────────────────────────╯

✓ OK Strategize in progress
Status of a plan that encountered an error:

$ agents plan status 01HXM7K2ZK4Q7C2B3F2R4VYV6J

╭─ Plan Status ────────────────────╮
│ Plan: 01HXM7K2ZK4Q7C2B3F2R4VYV6J │
│ Phase: execute                   │
│ State: errored                   │
│ Action: local/migrate-db         │
│ Project: local/api-service       │
│ Automation: supervised           │
│ Attempt: 1                       │
╰──────────────────────────────────╯

╭─ Progress ───────────╮
│  Strategize         │
│  Execute            │
│  Apply (skipped)    │
╰──────────────────────╯

╭─ Error Detail ──────────────────────────────────────────────────╮
│ Error: Tool invocation failed: connection refused               │
│ Tool: local/db-migrate                                          │
│ Step: 4 of 6                                                    │
│ Checkpoint: cp_01HXM8C2 (sandbox state preserved)               │
│ Recoverable: yes — use "agents plan prompt" to provide guidance │
╰─────────────────────────────────────────────────────────────────╯

╭─ Cost ───────────────╮
│ Tokens Used: 8,340   │
│ Cost So Far: $0.028  │
╰──────────────────────╯

✗ ERROR Plan errored — use `agents plan prompt` to resume or `agents plan cancel` to abort
##### agents plan cancel **Purpose** Cancel a plan that is not terminal. **Arguments** - ``: Plan ID. - `--reason/-r TEXT`: Optional reason. **Examples**

$ agents plan cancel 01HXM8C2ZK4Q7C2B3F2R4VYV6J --reason "blocked on credentials"

╭─ Plan Cancelled ─────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Phase: execute                   │
│ Reason: blocked on credentials   │
│ State: cancelled                 │
│ Cancelled At: 13:02:15           │
╰──────────────────────────────────╯

╭─ Sandbox ────────────────╮
│ Status: preserved        │
│ Files Modified: 3        │
│ Checkpoints: 2           │
╰──────────────────────────╯

╭─ Child Plans ─────────────╮
│ Completed: 1              │
│ Cancelled: 1              │
│ Artifacts Preserved: yes  │
╰───────────────────────────╯

╭─ Recovery ───────────────────────────────────────────╮
│ - Resolve credentials                                │
│ - Run agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
╰──────────────────────────────────────────────────────╯

✓ OK Plan cancelled
##### agents plan tree **Purpose** Render the decision tree for a plan. **Arguments** - ``: Plan ID (required). - `--show-superseded`: Include superseded decisions. **Examples**

$ agents plan tree 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Decision Tree ──────────────────────────────────────────────────────────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J                                                         │
│ ├─ [prompt_definition] "Increase test coverage to 85%"                                   │
│ ├─ [invariant_enforced] "Prioritize financial transaction and user mgmt code"            │
│ ├─ [invariant_enforced] "All API calls over TCP must be mocked"                          │
│ ├─ [strategy_choice] "Prioritize auth and payments" (confidence: 0.82)                   │
│ ├─ [subplan_parallel_spawn] "Implement auth and payment modules, in parallel"            │
│ │  ├─ [subplan_spawn] "Write auth tests" → Plan: 01HXM9F1A                               │
│ │  └─ [subplan_spawn] "Write payment tests" → Plan: 01HXM9F2B                            │
│ └─ [subplan_parallel_spawn] "Write tests for remaining modules"                          │
│    └─ ...                                                                                │
╰──────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Tree Summary ─────────────╮
│ Nodes: 9                   │
│ Depth: 3                   │
│ Child Plans: 2+            │
│ Invariants: 2              │
│ Superseded: 0 (hidden)     │
╰────────────────────────────╯

╭─ Child Plans ──────────────────────────────────────╮
│ ID          Name           Phase    State          │
│ ──────────  ─────────────  ───────  ─────────      │
│ 01HXM9F1A   auth-tests     execute  processing     │
│ 01HXM9F2B   payment-tests  execute  queued         │
╰────────────────────────────────────────────────────╯

╭─ Decision IDs (for correction) ──────────────────╮
│ Root: 01HXM9A0B1Q2W3R5G8Z0P4Q1X8                 │
│ Invariant 1: 01HXM9A0C1R3X4S6G9Z1P5Q2Y9          │
│ Invariant 2: 01HXM9A0D2S4Y5T7H0Z2P6Q3Z0          │
│ Strategy: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9             │
│ Parallel 1: 01HXM9A1D3R8X5S7H1Z2P5Q2Y0           │
│ Spawn Auth: 01HXM9A2D3Q8W4R6H9Z1P5Q2X0           │
│ Spawn Payment: 01HXM9A3E4Q9W5R7I0Z2P6Q3X1        │
│ Parallel 2: 01HXM9A4F5Q0W6R8J1Z3P7Q4X2           │
╰──────────────────────────────────────────────────╯

✓ OK Decision tree rendered
##### agents plan explain **Purpose** Show a detailed explanation for a decision. **Arguments** - ``: Decision ID. - `--show-context`: Include the context snapshot. - `--show-reasoning`: Include raw model reasoning if available. **Examples**

$ agents plan explain 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --show-context

╭─ Decision ─────────────────────────────────────╮
│ ID: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9                 │
│ Type: strategy_choice                          │
│ Question: Which modules should be prioritized? │
│ Chosen: Auth and payments                      │
│ Confidence: 0.82                               │
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J               │
│ Sequence: 2 of 5                               │
│ Created: 2026-02-08 12:58                      │
╰────────────────────────────────────────────────╯

╭─ Alternatives Considered ──────────────────────╮
│ 1. Auth and payments (chosen)                  │
│ 2. User module first (coverage 71%, med risk)  │
│ 3. All modules equally (spread thin)           │
╰────────────────────────────────────────────────╯

╭─ Impact ──────────────────────╮
│ Downstream Decisions: 3       │
│ Downstream Child Plans: 2     │
│ Artifacts Produced: 5         │
│ Correction Impact: medium     │
╰───────────────────────────────╯

╭─ Context Snapshot ───────────────╮
│ - Coverage < 70% in auth         │
│ - Payments failures last release │
│ - Auth: 12 files, 45% coverage   │
│ - Payments: 8 files, 52% cover.  │
│ Hot Context Hash: sha256:4b2e... │
╰──────────────────────────────────╯

╭─ Rationale ───────────────────────────────────────╮
│ Auth and payment modules have the lowest coverage │
│ and highest business risk. Auth handles security  │
│ tokens, payments handles money. Both had bugs in  │
│ the last release traceable to missing tests.      │
╰───────────────────────────────────────────────────╯

╭─ Correction ──────────────────────────────────────────────╮
│ agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9            │
│   --mode revert --guidance "Prioritize payments first..." │
╰───────────────────────────────────────────────────────────╯

✓ OK Decision explained
Including the raw model reasoning with `--show-reasoning`:

$ agents plan explain --show-reasoning 01HXM9A1C2Q7W3R5G8Z0P4Q1X9

╭─ Decision ──────────────────────────────╮
│ ID: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9          │
│ Type: strategy_choice                   │
│ Choice: Convert to async/await          │
│ Alternatives: 3                         │
╰─────────────────────────────────────────╯

╭─ Alternatives Considered ────────────────────────────────────╮
│ 1. Convert to async/await patterns (chosen)                  │
│    2. Keep synchronous with thread pool                      │
│    3. Use callback-based approach                            │
╰──────────────────────────────────────────────────────────────╯

╭─ Rationale ──────────────────────────────────────────────────╮
│ Async/await is the modern Python standard for I/O-bound      │
│ operations. The project already uses asyncio in 3 modules.   │
│ Thread pools would add complexity without native support.    │
╰──────────────────────────────────────────────────────────────╯

╭─ Model Reasoning (raw) ───────────────────────────────────────╮
│ I need to decide on the concurrency pattern for the payment   │
│ processing module. Let me analyze the current codebase:       │
│                                                               │
│ 1. src/payments/api.py uses synchronous requests.             │
│ 2. src/core/scheduler.py already uses asyncio.                │
│ 3. The database driver (asyncpg) supports async natively.     │
│ 4. Project invariant says "prefer modern Python patterns".    │
│                                                               │
│ Given that 3/5 core modules already use asyncio, and the      │
│ database driver supports it, converting to async/await is     │
│ the most consistent choice. Thread pools would work but       │
│ add unnecessary complexity and don't integrate well with      │
│ the existing asyncio event loop in scheduler.py.              │
╰───────────────────────────────────────────────────────────────╯

✓ OK Decision explained
##### agents plan correct **Purpose** Correct a decision either by reverting and re-executing or by appending a fix. **Arguments** - ``: Decision ID. - `--mode revert|append`: Correction mode. - `--guidance/-g TEXT`: Guidance text. - `--dry-run`: Show impact without executing. - `--yes`: Skip confirmation for revert mode. **Examples**

$ agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --mode revert \
  --guidance "Prioritize payments first" --yes

╭─ Correction ────────────────────────────────────────╮
│ Mode: revert                                        │
│ Impact: 3 decisions, 2 child plans, 5 artifacts     │
│ New Decision: 01HXM9B7Z3Q1Q8K2E9H7K3W2M8            │
│ Corrects: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9                │
│ Attempt: 2                                          │
╰─────────────────────────────────────────────────────╯

╭─ Affected Subtree ──────────────╮
│ Decisions Invalidated: 3        │
│ Child Plans Rolled Back: 2      │
│ Artifacts Archived: 5           │
│ Unaffected Decisions: 2         │
╰─────────────────────────────────╯

╭─ Sandbox Rollback ─────────────╮
│ Checkpoint: cp_01HXM8C2        │
│ Files Reverted: 5              │
│ Status: restored               │
╰────────────────────────────────╯

╭─ Recompute ──────────────╮
│ Queued: 2 child plans    │
│ ETA: 4m                  │
╰──────────────────────────╯

╭─ History ───────────────────────────────────────────╮
│ - Original decision superseded                      │
│ - Prior artifacts archived for comparison           │
│ - agents plan diff --correction 01HXM9B7Z3Q1Q8K2..  │
╰─────────────────────────────────────────────────────╯

✓ OK Correction applied
Using `--mode append` to add a corrective decision without reverting existing work (useful when the original decision was partially correct):

$ agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --mode append \
  --guidance "Also add rate limiting to the auth endpoints"

╭─ Correction ─────────────────────────────────────╮
│ Mode: append                                     │
│ Impact: adds to existing subtree, no rollback    │
│ New Decision: 01HXM9C3Z5T2Q8K2E9H7K3W2M8         │
│ Appended After: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9       │
│ Attempt: 2                                       │
╰──────────────────────────────────────────────────╯

╭─ Append Detail ─────────────────────────────────────────────────╮
│ Original decision preserved: yes                                │
│ Existing artifacts kept: yes                                    │
│ Additional work: appended as new child plan                     │
│ The original 5 artifacts remain; a new child plan will add      │
│ rate-limiting code on top of the existing auth changes.         │
╰─────────────────────────────────────────────────────────────────╯

╭─ Queued ──────────╮
│ New child: 1      │
│ ETA: 2m           │
╰───────────────────╯

✓ OK Append correction queued
Using `--dry-run` to preview the impact of a correction before committing to it:

$ agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --mode revert \
  --guidance "Use async/await pattern instead" --dry-run

╭─ Dry Run — Correction Preview ─────────────────────────────────────╮
│ ⚠  This is a preview only. No changes will be made.                │
╰────────────────────────────────────────────────────────────────────╯

╭─ Would Revert ───────────────────────────────────────╮
│ Decisions to invalidate: 3                           │
│   01HXM9A1..  strategy_choice    "sync pattern"      │
│   01HXM9A2..  implementation_choice  "requests"      │
│   01HXM9A3..  tool_invocation    write_file ×4       │
│ Child plans to roll back: 2                          │
│ Artifacts to archive: 5 files                        │
│ Unaffected decisions: 2 (will be kept)               │
╰──────────────────────────────────────────────────────╯

╭─ Estimated Cost ──────────╮
│ Re-strategize: ~$0.012    │
│ Re-execute: ~$0.035       │
│ Total: ~$0.047            │
│ ETA: ~4 minutes           │
╰───────────────────────────╯

To execute this correction, remove --dry-run and add --yes
##### agents plan diff **Purpose** Show diffs for a plan or a correction attempt. **Arguments** - ``: Show diff for a plan (positional argument). Mutually exclusive with --correction. - `--correction CORRECTION_ATTEMPT_ID`: Show diff for a correction attempt instead. **Examples**

$ agents plan diff 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Diff Summary ─────────────────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J               │
│ Project: local/api-service                     │
│ Files Changed: 2                               │
│ Insertions: 12                                 │
│ Deletions: 4                                   │
│ Net Change: +8 lines                           │
╰────────────────────────────────────────────────╯

╭─ Files ───────────────────────────────╮
│ Path                 Change  Status   │
│ ───────────────────  ──────  ──────── │
│ src/auth/session.py  +8 -2   modified │
│ src/auth/tokens.py   +4 -2   modified │
╰───────────────────────────────────────╯

╭─ Patch Preview ──────────────────────────╮
│ --- a/src/auth/session.py                │
│ +++ b/src/auth/session.py                │
│ @@ -12,4 +12,10 @@                       │
│ - import jwt                             │
│ + import sessionlib                      │
│ - def validate_token(...)                │
│ + def validate_session(...)              │
│ --- a/src/auth/tokens.py                 │
│ +++ b/src/auth/tokens.py                 │
│ @@ -5,3 +5,7 @@                          │
│ - TOKEN_EXPIRY = 3600                    │
│ + TOKEN_EXPIRY = 7200                    │
╰──────────────────────────────────────────╯

╭─ Risk Assessment ────────────────╮
│ API Compatibility: preserved     │
│ Test Coverage: maintained        │
│ Breaking Changes: none detected  │
╰──────────────────────────────────╯

✓ OK Diff generated
Showing the diff for a specific correction attempt, comparing what changed between the original and corrected execution:

$ agents plan diff --correction 01HXM9B7Z3Q1Q8K2E9H7K3W2M8

╭─ Correction Diff ───────────────────────────────╮
│ Correction: 01HXM9B7Z3Q1Q8K2E9H7K3W2M8          │
│ Original Decision: 01HXM9A1C2Q7W3R5..           │
│ Mode: revert                                    │
│ Files Changed: 3                                │
│ New Insertions: 18                              │
│ New Deletions: 6                                │
╰─────────────────────────────────────────────────╯

╭─ Comparison ─────────────────────────────────────────────────────╮
│ File                  Before (original)  After (corrected)       │
│ ────────────────────  ────────────────  ────────────────────     │
│ src/payments/api.py   +12 -4            +18 -6 (expanded)        │
│ src/auth/tokens.py    +8 -2             (unchanged)              │
│ tests/test_payments.py  (new file)      (new file, larger)       │
╰──────────────────────────────────────────────────────────────────╯

╭─ Patch Preview (corrected vs original) ───────────╮
│ --- a/src/payments/api.py (original)              │
│ +++ b/src/payments/api.py (corrected)             │
│ @@ -1,12 +1,18 @@                                 │
│ - # sync payment processing                       │
│ + # async payment processing (corrected)          │
│ + import asyncio                                  │
│ + from aiohttp import ClientSession               │
│   ...                                             │
╰───────────────────────────────────────────────────╯

✓ OK Correction diff generated
##### agents plan artifacts **Purpose** List artifacts produced by a plan. **Arguments** - ``: Plan ID. **Examples**

$ agents plan artifacts 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Artifacts ─────────────────────────────────────────────────╮
│ Path                   Type   Size    Change     Child Plan │
│ ─────────────────────  ─────  ──────  ─────────  ───────    │
│ src/auth/session.py    write  2.1 KB  +8 -2      root       │
│ tests/test_session.py  write  4.7 KB  +47 -0     root       │
│ src/auth/tokens.py     edit   1.8 KB  +4 -2      root       │
│ tests/test_tokens.py   write  3.2 KB  +32 -0     auth       │
╰─────────────────────────────────────────────────────────────╯

╭─ Summary ───────────╮
│ Total: 4            │
│ Writes: 2 (new)     │
│ Edits: 2 (modified) │
│ Deletes: 0          │
│ Total Size: 11.8 KB │
╰─────────────────────╯

╭─ By Plan ─────────────────╮
│ Root Plan: 3 artifacts    │
│ auth-tests: 1 artifact    │
│ payment-tests: (pending)  │
╰───────────────────────────╯

✓ OK 4 artifacts listed
##### agents plan prompt **Purpose** Provide additional guidance to a plan, typically when it is errored or awaiting input. **Arguments** - ``: Plan ID. - `""`: Guidance text. **Examples**

$ agents plan prompt 01HXM8C2ZK4Q7C2B3F2R4VYV6J "Use mocks for database tests"

╭─ Guidance Added ────────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J        │
│ Guidance: Use mocks for database tests  │
│ Scope: next execution step              │
│ Phase: execute                          │
│ State: errored → processing             │
╰─────────────────────────────────────────╯

╭─ Decision Created ────────────────────────╮
│ Type: user_intervention                   │
│ ID: 01HXM9C5G7R2X8S3K4Z5Q8R6Y3            │
│ Parent: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9        │
╰───────────────────────────────────────────╯

╭─ Queue ────╮
│ Pending: 1 │
│ Applied: 0 │
╰────────────╯

✓ OK Guidance queued
##### agents plan rollback **Purpose** Rollback a plan sandbox to a checkpoint. **Arguments** - ``: Plan ID. - ``: Checkpoint ID. - `--yes`: Skip confirmation. **Examples**

$ agents plan rollback 01HXM8C2ZK4Q7C2B3F2R4VYV6J cp_01HXM8C2

Rollback plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J to cp_01HXM8C2? [y/N]: y

╭─ Rollback Summary ───────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Checkpoint: cp_01HXM8C2          │
│ Label: before auth refactor      │
│ Files: 6 reverted                │
╰──────────────────────────────────╯

╭─ Changes Reverted ──────────────────╮
│ File                    Action      │
│ ──────────────────────  ──────────  │
│ src/auth/session.py     restored    │
│ src/auth/tokens.py      restored    │
│ tests/test_session.py   removed     │
│ tests/test_tokens.py    removed     │
│ src/auth/fixtures.py    restored    │
│ src/auth/__init__.py    restored    │
╰─────────────────────────────────────╯

╭─ Impact ──────────────────────────────╮
│ Child Plans Invalidated: 2            │
│ Sandbox: restored to cp_01HXM8C2      │
│ Decisions After CP: 2 discarded       │
│ Tool Calls After CP: 5 undone         │
╰───────────────────────────────────────╯

╭─ Post-Rollback State ──────────╮
│ Phase: execute                 │
│ State: queued (awaiting input) │
│ Checkpoints Remaining: 2       │
╰────────────────────────────────╯

✓ OK Rollback complete
#### agents action **Purpose** Manage reusable actions. ##### agents action create **Purpose** Create a new action template from a YAML configuration file. The `--config` file is required and must fully define the action. CLI options provided alongside `--config` act as optional overrides for values defined in the configuration file. **Arguments** - `[]`: Namespaced action name (optional when provided in config file). - `--config/-c FILE`: YAML configuration file defining the action (required). The file must fully define the action. Any CLI options provided alongside `--config` override the corresponding values in the file. - `--strategy-actor ACTOR`: Override the Strategize actor from config. - `--execution-actor ACTOR`: Override the Execution actor from config. - `--definition-of-done TEXT`: Override the completion criteria from config. - `--description TEXT`: Short description. - `--long-description TEXT`: Long description. - `--arg/-a spec`: Argument definition (repeatable). - `--reusable/--no-reusable`: Keep action after use. - `--read-only`: Read-only action. - `--available`: Make action available immediately. - `--estimation-actor ACTOR`: Optional estimation actor. - `--invariant-actor ACTOR`: Invariant Reconciliation Actor for this action. Carried forward to plans created from this action (can be overridden via `agents plan use --invariant-actor`). - `--automation-profile PROFILE`: Default automation profile for plans created from this action. - `--invariant TEXT`: Invariant to attach to this action (repeatable). These invariants are carried forward as plan-level invariants when the action is used. The `--config` file must define `strategy-actor`, `execution-actor`, and `definition-of-done`. Any of these can be overridden via CLI flags. **Examples**

$ agents action create --config ./actions/code-coverage.yaml \
  --available local/code-coverage

╭─ Action Created ──────────────────────╮
│ Name: local/code-coverage             │
│ ID: 01HXMAY3D1JQ0C3G1H0Q7B2W7M        │
│ State: available                      │
│ Strategy Actor: local/strategist      │
│ Execution Actor: local/executor       │
│ Reusable: yes                         │
│ Read Only: no                         │
│ Created: 2026-02-08 12:20             │
╰───────────────────────────────────────╯

╭─ Definition of Done ─╮
│ Coverage reaches 85% │
╰──────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────╮
│ Name                     Type    Required  Description           │
│ ───────────────────────  ──────  ────────  ───────────────────── │
│ target_coverage_percent  int     yes       Target coverage %     │
│ test_command             string  no        Test framework to use │
╰──────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮
│ Profile: supervised                   │
│ Source: default                       │
╰───────────────────────────────────────╯

╭─ Usage ──────────────────────────────────────────────────────────────────╮
│ agents plan use local/code-coverage local/api-service                    │
│   --arg target_coverage_percent=85                                       │
╰──────────────────────────────────────────────────────────────────────────╯

✓ OK Action created
Creating an action entirely from a YAML configuration file:

$ agents action create local/code-coverage --config ./actions/code-coverage.yaml

╭─ Action Created ────────────────────────╮
│ Name: local/code-coverage               │
│ ID: 01HXMAY3D1JQ0C3G1H0Q7B2W7M          │
│ State: draft                            │
│ Strategy Actor: local/strategist        │
│ Execution Actor: local/executor         │
│ Reusable: yes                           │
│ Read Only: no                           │
│ Config: ./actions/code-coverage.yaml    │
│ Created: 2026-02-08 12:20               │
╰─────────────────────────────────────────╯

╭─ Definition of Done ─╮
│ Coverage reaches 85% │
╰──────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────╮
│ Name                     Type    Required  Description           │
│ ───────────────────────  ──────  ────────  ───────────────────── │
│ target_coverage_percent  int     yes       Target coverage %     │
│ test_command             string  no        Test framework to use │
╰──────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮
│ Profile: supervised                   │
│ Source: default                       │
╰───────────────────────────────────────╯

✓ OK Action created
Creating an action from a config file with CLI overrides (the `--execution-actor` overrides the value from the YAML, and `--available` is added):

$ agents action create local/code-coverage \
  --config ./actions/code-coverage.yaml \
  --execution-actor local/fast-executor \
  --available

╭─ Action Created ────────────────────────╮
│ Name: local/code-coverage               │
│ ID: 01HXMAY3D1JQ0C3G1H0Q7B2W7M          │
│ State: available                        │
│ Strategy Actor: local/strategist        │
│ Execution Actor: local/fast-executor    │
│ Reusable: yes                           │
│ Read Only: no                           │
│ Config: ./actions/code-coverage.yaml    │
│ Created: 2026-02-08 12:20               │
╰─────────────────────────────────────────╯

╭─ Overrides Applied ──────────────────────────────────────╮
│ execution_actor: local/executor → local/fast-executor    │
│ available: false → true                                  │
╰──────────────────────────────────────────────────────────╯

╭─ Definition of Done ─╮
│ Coverage reaches 85% │
╰──────────────────────╯

✓ OK Action created
##### agents action list **Purpose** List actions with optional filters. **Arguments** - `--namespace/-n NS`: Filter by namespace. - `--state/-s STATE`: Filter by state. - `--available`: Show only available actions. **Examples**

$ agents action list --available

╭─ Actions ──────────────────────────────────────────────────────────────────────────────────╮
│ Name                 State      Strategy Actor    Execution Actor  Reusable  Plans         │
│ ───────────────────  ─────────  ────────────────  ───────────────  ────────  ─────         │
│ local/code-coverage  available  local/strategist  local/executor   ✓         3             │
╰────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ────────╮
│ State: available │
│ Namespace: (any) │
╰──────────────────╯

╭─ Summary ──────────────╮
│ Total: 1               │
│ Available: 1           │
│ Draft: 0               │
│ Archived: 0            │
│ Total Plans Created: 3 │
╰────────────────────────╯

✓ OK 1 action listed
##### agents action show **Purpose** Show details for an action. **Arguments** - ``: Action name. **Examples**

$ agents action show local/code-coverage

╭─ Action Details ──────────────────────╮
│ Name: local/code-coverage             │
│ ID: 01HXMAY3D1JQ0C3G1H0Q7B2W7M        │
│ State: available                      │
│ Strategy Actor: local/strategist      │
│ Execution Actor: local/executor       │
│ Reusable: yes                         │
│ Read Only: no                         │
│ Created: 2026-02-08 12:20             │
╰───────────────────────────────────────╯

╭─ Definition of Done ─╮
│ Coverage reaches 85% │
╰──────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────╮
│ Name                     Type    Required  Description           │
│ ───────────────────────  ──────  ────────  ───────────────────── │
│ target_coverage_percent  int     yes       Target coverage %     │
│ test_command             string  no        Test framework to use │
╰──────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮
│ Profile: supervised                   │
│ Source: default                       │
╰───────────────────────────────────────╯

╭─ History ────────────────────╮
│ Plans Created: 3             │
│ Plans Completed: 2           │
│ Plans Failed: 0              │
│ Avg Duration: 00:04:30       │
│ Avg Cost: $0.072             │
╰──────────────────────────────╯

╭─ Usage ───────────────────────────────────────────────────────────╮
│ - agents plan use local/code-coverage local/api-service           │
│     --arg target_coverage_percent=85                              │
╰───────────────────────────────────────────────────────────────────╯

✓ OK Action loaded
##### agents action available **Purpose** Mark a draft action as available. **Arguments** - ``: Action name. **Examples**

$ agents action available 01HXMAY3D1JQ0C3G1H0Q7B2W7M

╭─ Action Available ─────────────╮
│ ID: 01HXMAY3D1JQ0C3G1H0Q7B2W7M │
│ State: draft → available       │
│ Name: local/code-coverage      │
╰────────────────────────────────╯

╭─ Visibility ─────╮
│ Namespace: local │
│ Listed: yes      │
│ Usable: yes      │
╰──────────────────╯

╭─ Validation ────────────────────────╮
│ Strategy Actor: resolved            │
│ Execution Actor: resolved           │
│ Definition of Done: present         │
│ Arguments: valid schema             │
╰─────────────────────────────────────╯

✓ OK Action marked available
##### agents action archive **Purpose** Archive an action. **Arguments** - ``: Action name. **Examples**

$ agents action archive local/old-action

╭─ Action Archived ───────────╮
│ Name: local/old-action      │
│ State: available → archived │
│ Archived: 2026-02-08 12:22  │
╰─────────────────────────────╯

╭─ Impact ───────────────────────╮
│ Availability: hidden from list │
│ Existing Plans: unchanged      │
│ Active Plans: 0 affected       │
╰────────────────────────────────╯

╭─ History ─────────────────╮
│ Total Plans: 5            │
│ Completed: 4              │
│ Failed: 1                 │
│ Last Used: 2026-02-06     │
╰───────────────────────────╯

✓ OK Action archived
#### agents automation-profile **Purpose** Manage automation profiles — named collections of boolean flags that control which tasks are automated vs. require human approval. Built-in profiles (`locked-down`, `manual`, `supervised`, `trusted`, `autonomous`, `full-auto`) are always available. Custom profiles follow the same `/` naming convention as other entities. ##### agents automation-profile add **Purpose** Register a new custom automation profile from a YAML configuration file. If a profile with the same name already exists, the command fails unless the `--update` flag is provided. **Arguments** - `[]`: Profile name (optional when provided in config file). - `--config/-c FILE`: YAML configuration file defining the profile (required). - `--update`: Replace an existing profile with the same name. **Examples**

$ agents automation-profile add --config ./profiles/careful-auto.yaml

╭─ Profile Registered ─────────────────────────────────────────────╮
│ Name: local/careful-auto                                         │
│ Description: Autonomous execution with mandatory sandbox         │
│              and manual apply                                    │
│ Created: 2026-02-08 14:30                                        │
╰──────────────────────────────────────────────────────────────────╯

╭─ Flags ────────────────────────────────╮
│ auto_strategize: true                  │
│ auto_execute: true                     │
│ auto_apply: false                      │
│ auto_decisions_strategize: true        │
│ auto_decisions_execute: true           │
│ auto_validation_fix: true              │
│ auto_strategy_revision: false          │
│ auto_child_plans: true                 │
│ auto_retry_transient: true             │
│ auto_checkpoint_restore: true          │
│ require_sandbox: true                  │
│ require_checkpoints: true              │
│ allow_unsafe_tools: false              │
╰────────────────────────────────────────╯

✓ OK Profile registered
##### agents automation-profile remove **Purpose** Remove a custom automation profile. Built-in profiles cannot be removed. **Arguments** - ``: Profile name. - `--yes, -y`: Skip confirmation prompt. **Examples**

$ agents automation-profile remove local/careful-auto

Remove automation profile local/careful-auto? [y/N]: y

╭─ Profile Removed ──────────╮
│ Name: local/careful-auto   │
╰────────────────────────────╯

✓ OK Profile removed
##### agents automation-profile list **Purpose** List all available automation profiles (built-in and custom). **Arguments** - `[REGEX]`: Optional filter pattern. **Examples**

$ agents automation-profile list

╭─ Automation Profiles ─────────────────────────────────────────────────────────╮
│ Name                Source    Auto-Apply  Sandbox  Description                │
│ ──────────────────  ────────  ──────────  ───────  ────────────────────────   │
│ locked-down        built-in  no          yes      Maximum human control       │
│ manual             built-in  no          yes      Human-driven (default)      │
│ supervised         built-in  no          yes      Auto-plan, manual exec      │
│ trusted            built-in  no          yes      Auto-exec, manual apply     │
│ autonomous         built-in  no          yes      Full auto except apply      │
│ full-auto          built-in  yes         no       Complete automation         │
│ local/careful-auto custom    no          yes      Custom careful profile      │
╰───────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ───────────╮
│ Built-in: 6         │
│ Custom: 1           │
│ Total: 7            │
╰─────────────────────╯

✓ OK 7 profiles listed
##### agents automation-profile show **Purpose** Show full details for an automation profile, including all flag values. **Arguments** - ``: Profile name. **Examples**

$ agents automation-profile show trusted

╭─ Automation Profile ───────────────────────────────────────────╮
│ Name: trusted                                                  │
│ Source: built-in                                               │
│ Description: Auto-exec, manual apply. Day-to-day development   │
╰────────────────────────────────────────────────────────────────╯

╭─ Phase Transitions ──────────────────╮
│ auto_strategize: true                │
│ auto_execute: true                   │
│ auto_apply: false                    │
╰──────────────────────────────────────╯

╭─ Decision Automation ────────────────╮
│ auto_decisions_strategize: true      │
│ auto_decisions_execute: true         │
╰──────────────────────────────────────╯

╭─ Self-Repair ────────────────────────╮
│ auto_validation_fix: true            │
│ auto_strategy_revision: false        │
│ auto_retry_transient: true           │
│ auto_checkpoint_restore: false       │
╰──────────────────────────────────────╯

╭─ Execution Controls ─────────────────╮
│ auto_child_plans: true               │
│ require_sandbox: true                │
│ require_checkpoints: true            │
│ allow_unsafe_tools: false            │
╰──────────────────────────────────────╯

✓ OK Profile loaded
#### agents config **Purpose** Manage global configuration values. ##### agents config set **Purpose** Set a configuration key. **Arguments** - ``: automation-profile, log-level, invariant-actor, format. - ``: Value to set. The `invariant-actor` key sets the default Invariant Reconciliation Actor used globally. This actor is used to reconcile invariant conflicts when neither the plan nor the project specifies one. The `format` key sets the default output rendering format used by all commands. Accepted values are `rich`, `color`, `table`, `plain`, `json`, `yaml`. When set, this value is used unless overridden by the `--format` CLI flag. See **Output Rendering Framework** for details. **Examples**

$ agents config set automation-profile trusted

╭─ Config Updated ───────────────╮
│ Key: automation-profile        │
│ Value: trusted                 │
│ Previous: manual               │
│ Source: config                 │
│ Scope: global                  │
╰────────────────────────────────╯

╭─ Effective ────────────────────────╮
│ Sessions: new sessions             │
│ Plans: future plans (unless set)   │
│ Existing: unchanged                │
╰────────────────────────────────────╯

╭─ Saved To ──────────────────────────╮
│ File: ~/.cleveragents/config.toml   │
│ Line: 8                             │
╰─────────────────────────────────────╯

✓ OK Config updated
Setting the default output format:

$ agents config set format table

╭─ Config Updated ─────────────────╮
│ Key: format                      │
│ Previous: rich                   │
│ New Value: table                 │
│ Scope: user (~/.cleveragents)    │
╰──────────────────────────────────╯

✓ OK Set format = table
Setting a global invariant actor:

$ agents config set invariant-actor local/invariant-resolver

╭─ Config Updated ──────────────────────────╮
│ Key: invariant-actor                      │
│ Previous: (not set)                       │
│ New Value: local/invariant-resolver       │
│ Scope: user (~/.cleveragents)             │
╰───────────────────────────────────────────╯

✓ OK Set invariant-actor = local/invariant-resolver
##### agents config get **Purpose** Get a configuration value. **Arguments** - ``: Key to read. **Examples**

$ agents config get automation-profile

╭─ Config ───────────────────╮
│ Key: automation-profile    │
│ Value: trusted             │
│ Source: config             │
│ Overridden: no             │
│ Type: string               │
╰────────────────────────────╯

╭─ Origin ──────────────────────────╮
│ File: ~/.cleveragents/config.toml │
│ Line: 8                           │
│ Default: full                     │
╰───────────────────────────────────╯

╭─ Resolution Chain ──────────────╮
│ 1. CLI flag: (not set)          │
│ 2. Env var: (not set)           │
│ 3. Config file: review          │
│ 4. Default: full                │
│ Winner: config file (level 3)   │
╰─────────────────────────────────╯

✓ OK Config read
##### agents config list **Purpose** List all configuration values. **Arguments** None. **Examples**

$ agents config list

╭─ Config ──────────────────────────────────────────────────╮
│ Key               Value               Source   Modified   │
│ ────────────────  ──────────────────  ───────  ────────   │
│ automation-profile trusted             config   yes       │
│ invariant-actor   local/reconciler    config   yes        │
│ log-level         INFO                default  no         │
╰───────────────────────────────────────────────────────────╯

╭─ Overrides ─────╮
│ Env: none       │
│ CLI Flags: none │
╰─────────────────╯

╭─ Config File ───────────────────────╮
│ Path: ~/.cleveragents/config.toml   │
│ Size: 284 bytes                     │
│ Valid: yes                          │
╰─────────────────────────────────────╯

✓ OK 6 settings listed
#### agents invariant **Purpose** Manage invariants — named constraints that guide and constrain plan execution. Invariants can be attached to any scope: global (all plans), project (all plans targeting that project), plan (a specific plan and its child plans), or action (carried forward when the action is used). Exactly one scope flag is required for `add` and `list`. ##### agents invariant add **Purpose** Add an invariant at the specified scope. **Arguments** - ``: The invariant text (positional argument at end of command). - `--global`: Attach as a global invariant (applies to all plans). - `--project/-p PROJECT`: Attach to a project (applies to all plans targeting this project). - `--plan PLAN_ID`: Attach to a plan (plan-level invariant). Repeatable. - `--action ACTION`: Attach to an action (action-level invariant). Repeatable. At least one scope flag (`--global`, `--project`, `--plan`, or `--action`) must be provided. `--plan` and `--action` can be repeated to attach the same invariant to multiple plans or actions. **Examples**

$ agents invariant add --global "All public APIs must maintain backward compatibility"

╭─ Invariant Added ──────────────────────────────────────────────────────╮
│ Invariant: All public APIs must maintain backward compatibility        │
│ Scope: global                                                          │
│ ID: inv_01HXM9A1B                                                      │
╰────────────────────────────────────────────────────────────────────────╯

✓ OK Invariant added

$ agents invariant add --project local/api-service "All endpoints must validate auth tokens"

╭─ Invariant Added ──────────────────────────────────────────────╮
│ Project: local/api-service                                     │
│ Invariant: All endpoints must validate auth tokens             │
│ Scope: project                                                 │
│ ID: inv_01HXM9A2C                                              │
╰────────────────────────────────────────────────────────────────╯

✓ OK Invariant added

$ agents invariant add --plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J "All database queries must use parameterized statements"

╭─ Invariant Added ─────────────────────────────────────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J                                      │
│ Invariant: All database queries must use parameterized statements     │
│ Scope: plan                                                           │
│ ID: inv_01HXM9G3A                                                     │
╰───────────────────────────────────────────────────────────────────────╯

✓ OK Invariant added

$ agents invariant add --action local/code-coverage "Test files must not import production secrets"

╭─ Invariant Added ──────────────────────────────────────────────────────╮
│ Action: local/code-coverage                                            │
│ Invariant: Test files must not import production secrets               │
│ Scope: action                                                          │
│ ID: inv_01HXM9H4B                                                      │
╰────────────────────────────────────────────────────────────────────────╯

✓ OK Invariant added
##### agents invariant list **Purpose** List invariants at a given scope. Use `--effective` with `--plan` to show the final reconciled view of invariants (after precedence resolution) rather than just the invariants directly attached at that scope. **Arguments** - `--global`: List global invariants. - `--project/-p PROJECT`: List invariants attached to a project. - `--plan PLAN_ID`: List invariants attached to a plan. - `--action ACTION`: List invariants attached to an action. - `--effective`: (Only with `--plan`) Show the reconciled invariant view after precedence resolution across all scopes. **Examples**

$ agents invariant list --global

╭─ Global Invariants ──────────────────────────────────────────────────────╮
│ ID              Text                                                     │
│ ──────────────  ────────────────────────────────────────────────────     │
│ inv_01HXM9A1B  All public APIs must maintain backward compatibility      │
│ inv_01HXM9A1C  Payment processing must be idempotent                     │
╰──────────────────────────────────────────────────────────────────────────╯

✓ OK 2 invariants

$ agents invariant list --plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J --effective

╭─ Effective Invariants (Plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J) ──────────────────────────────────╮
│ ID              Source   Text                                                             │
│ ──────────────  ───────  ──────────────────────────────────────────────────────           │
│ inv_01HXM9A1B  global   All public APIs must maintain backward compatibility              │
│ inv_01HXM9A2C  project  All endpoints must validate auth tokens                           │
│ inv_01HXM9G3A  plan     All database queries must use parameterized statements            │
╰───────────────────────────────────────────────────────────────────────────────────────────╯

│ Conflicts Resolved: 1                                                              │
│   Global "Use shared DB pool" overridden by plan "All database queries must use    │
│   parameterized statements"✓ OK 3 effective invariants (1 global, 1 project, 1 plan; 1 conflict resolved)
##### agents invariant remove **Purpose** Remove an invariant by ID. The invariant is removed from whichever scope it was originally attached to. **Arguments** - ``: Invariant ID to remove. - `--yes`: Skip confirmation. **Examples**

$ agents invariant remove inv_01HXM9A1C

Remove invariant inv_01HXM9A1C ("Payment processing must be idempotent", scope: global)? [y/N]: y

╭─ Invariant Removed ──────────────────────────────────────────────╮
│ Removed: Payment processing must be idempotent                   │
│ Scope: global                                                    │
│ ID: inv_01HXM9A1C                                                │
╰──────────────────────────────────────────────────────────────────╯

✓ OK Invariant removed
## Core Concepts ### Plan A **plan** is the fundamental unit of orchestration and traceability. #### Plan Lifecycle Phases A plan always moves through the following phases, in order: **Action → Strategize → Execute → Apply → Applied (terminal)** In this spec: * **Strategize** is the phase name (the output is a **strategy**). * **Execute** is the phase name (the output is a **changeset**). * **Apply** is the phase name (the output is an **applied change**). * **Applied** is the resulting terminal state after Apply succeeds. This four-stage model is explicitly called out as the new architecture replacing a prior linear pipeline. #### Phase Transition Verbs (CLI / UX Contract) Verbs that trigger phase transitions. CleverAgents should standardize these verbs as the **public API** (CLI, TUI, web): | Current Phase | Command Verb | Next Phase | | ------------- | ------------ | ---------- | | (none) | `create` | Action | | Action | `use` | Strategize | | Strategize | `execute` | Execute | | Execute | `apply` | Applied | **Important behavioral rule:** CleverAgents uses **automation profiles** to control which of these phase transitions happen automatically. The profile determines whether each transition requires explicit user action or proceeds autonomously, but the verbs remain the conceptual contract. #### Plan States (Per Phase) A plan's phase indicates "what step of the lifecycle it is in." Separately, the plan has a **processing state** indicating "what is happening right now." Recommended state model: * **Action phase states** * `available` (action exists and can be used) * `draft` (action is being authored/edited) * `archived` (soft-deleted or hidden, optional) * **Strategize / Execute / Apply phase states** * `queued` (waiting for compute/worker) * `processing` (currently running) * `errored` (failed; includes error metadata) * `complete` (finished successfully) * `cancelled` (user/system cancelled; safe terminal for that phase) #### Plan Identity and Traceability Every plan should have: * **plan_id**: Unique, immutable ID (UUID or ULID). * **parent_plan_id**: Nullable; present for child plans. * **root_plan_id**: The top-most plan in the tree. * **attempt**: An integer attempt counter that increments when re-running a phase (e.g., re-executing after a fix). * **created_at / updated_at / completed_at** timestamps. * **created_by** (user identity / session identity). #### Plan Hierarchy and Parallelism A single plan should usually represent the smallest "complete" unit of work (similar to what would fit in one git commit). However: * **Plans are hierarchical.** A child plan is simply a Plan with a parent — it follows the same lifecycle, has the same data model, and is functionally identical to a root plan except that it has a `parent_plan_id`. * **Decisions about child plans are made during Strategize** (as `subplan_spawn` decision types). Multiple child plans that should execute concurrently are grouped under a `subplan_parallel_spawn` decision. * **Child plans are actually spawned during Execute** (based on those decisions). * Child plans can run **in parallel** (via `subplan_parallel_spawn`) or **sequentially** (via individual `subplan_spawn` decisions). Without a `subplan_parallel_spawn` wrapper, each `subplan_spawn` decision results in sequential execution. * **Applicable invariants are enforced** during Strategize by adding `invariant_enforced` decisions to the tree, which constrain downstream decisions and child plans. * The parent plan is responsible for **merging results**. This is core to the long-term objective: tackling large tasks while only recomputing parts of the decision tree when corrected. ##### Hierarchical Decomposition for Scale When handling massive tasks (e.g., converting Firefox to Rust), the system uses hierarchical decomposition. Each level spawns child plans (which are themselves full Plans with their own decision trees): 1. **Root Plan**: High-level architectural decisions and invariants - "Convert Firefox Renderer to Rust" - Invariant: "Maintain API compatibility with existing C++ callers" - Decision: "Start with leaf modules, work inward" - Context: Module dependency graph (2,847 modules) 2. **Subsystem-Level Plans**: Major component decisions (spawned via `subplan_parallel_spawn` for independent subsystems) - "Phase 1: Convert utility libraries (no external deps)" - Each subsystem plan gets its own bounded context and inherits parent invariants 3. **Module-Level Plans**: Individual module conversions - "Convert string_utils module" - Context: Only the 47 functions and 12 dependent files - Decision: "Use Rust's String type" 4. **File-Level Plans**: Specific file changes - Actual code transformations - Minimal context needed At each level, only the relevant context is loaded. The persistent decision graph means we can always reconstruct why we're converting a particular module and what constraints (including inherited invariants) apply from higher-level decisions. ##### Child Plan Spawning Mechanism In the actor definition for the execution actor, tool nodes can directly invoke registered tools to trigger child plans. The `local/create-subplan` tool is independently registered and referenced by name in the actor graph node. During execution, `subplan_spawn` decisions are realized as actual child plans, and `subplan_parallel_spawn` groups trigger concurrent spawning of all enclosed child plans.

# Example: Execution actor with subplan spawning capability.
# The graph uses a tool node referencing a named registered tool.
actors:
  code_executor:
    type: graph
    config:
      actor: anthropic/claude-3-opus
    skills:
      - local/plan-tools          # Skill containing create_subplan for LLM tool-calling
      - local/file-ops
    routes:
      execute_workflow:
        nodes:
          - name: spawn_test_subplan
            type: tool
            tool: local/create-subplan # Named tool from Tool Registry
The `local/create-subplan` tool is independently registered via its own YAML configuration file:

# File: tools/create-subplan.yaml
cleveragents:
  version: "3.0"

tool:
  name: local/create-subplan
  description: "Spawn a subplan for a given action"
  source: custom

  input_schema:
    type: object
    properties:
      action: { type: string }
      target_files: { type: array, items: { type: string } }
    required: [action]

  capability:
    writes: true
    checkpointable: false
    side_effects: [spawn_subplan]

  code: |
    subplan = ctx.spawn_subplan(
        action=params["action"],
        target_files=params.get("target_files", [])
    )
    return {"subplan_id": subplan.id}
The `local/plan-tools` skill references this tool (and others) by name:

# File: skills/plan-tools.yaml
skill:
  name: local/plan-tools
  description: "Tools for spawning and managing subplans"
  tools:
    - local/create-subplan
##### Child Plan Execution Modes * **Sequential**: Individual `subplan_spawn` decisions without a `subplan_parallel_spawn` wrapper execute one after another. If one fails, subsequent child plans are not started. * **Parallel**: Multiple `subplan_spawn` decisions grouped under a `subplan_parallel_spawn` decision execute concurrently. If one fails, others can continue. The `subplan_parallel_spawn` decision acts as a container that signals the system to spawn all enclosed child plans simultaneously. ##### Child Plan Failure Handling * **Parallel execution** (via `subplan_parallel_spawn`): Other parallel child plans continue even if one fails. * **Sequential execution**: Subsequent child plans are not started if a prior one fails. * **Note**: An "error" only occurs if an exception is thrown by the application (a bug). Plan failures (e.g., tests don't pass) are handled within the plan's logic, not as application errors. ##### Child Plan Result Merging The way child plan results are merged depends on the resource type: * **Git-compatible resources** (source code, text files): Git-style merge * **Databases**: Transaction coordination or sequential application * **Other resources**: Pluggable merge strategies based on resource type * **Non-mergeable resources**: May require sequential execution only #### The Plan "Decision Tree" and Visualization CleverAgents intends to record enough information to render: * an **ASCII tree** in the TUI, and * optionally a GUI tree via visualization tools (D3/Cytoscape) once the data exists. This implies each plan should persist: * decisions made, * the rationale (or at least the prompt/context snapshot that produced it), * dependencies ("this decision influenced these child plans"). This is required for "correcting plans" (see Behavior section). #### Decision Data Model ##### Relationship Between Plan Description and Decisions Each plan has a **description field** (inherited from the action's description, potentially with argument substitutions). This description acts as the **primary component of the prompt** fed to the strategy actor during the Strategize phase. **Decisions are choices that are NOT explicitly defined by the plan description.** They represent the gaps, ambiguities, or implementation details that must be resolved to execute the plan. For example: - **Plan description**: "Increase test coverage to 85%" - **Decisions that emerge**: - "Which modules should be prioritized?" (not specified in description) - "Should we use mocks or integration tests for the database layer?" (not specified) - "Should we refactor the auth module to make it more testable, or write tests around it as-is?" (not specified) ##### Decision Making Based on Autonomy Level Who makes decisions depends on the plan's **automation profile**: | Profile Flag | Who Makes Decisions | |-------------|---------------------| | `auto_decisions_strategize = false` | User is prompted for each decision point during Strategize | | `auto_decisions_strategize = true` | Strategy actor makes decisions autonomously, records reasoning | | `auto_decisions_execute = false` | User is prompted for each decision point during Execute | | `auto_decisions_execute = true` | Execution actor makes decisions autonomously | When **automation allows automatic decisions**, the strategy actor uses its best judgment based on context, and records its reasoning in the decision's `rationale` field. When **user input is required**, the system pauses and prompts the user:

Decision required: Which modules should be prioritized for test coverage?

Options identified by the strategy actor:
  1. auth module (currently 45% coverage, high risk)
  2. payment module (currently 52% coverage, high risk)  
  3. user module (currently 71% coverage, medium risk)

Your choice (or provide custom guidance): _
##### The Prompt as the Root Decision **The prompt passed to the strategize actor is itself a decision node** in the decision tree—specifically, it's the **root decision** of type `prompt_definition`. This is important because: 1. **Every plan has its own prompt**: The root plan's prompt comes from the action description + user arguments. Child plan prompts are created by parent plans during their execution. 2. **Parent plans create child plan prompts**: When a parent plan spawns a child plan, it decides what prompt to give that child plan. This is recorded as a `prompt_definition` decision in the parent's tree, and becomes the root decision of the child plan's tree. 3. **Invariants flow into the decision tree**: During Strategize, applicable invariants (from global, project, action, and plan scopes) are reconciled via the **Invariant Reconciliation Actor** and recorded as `invariant_enforced` decisions, making them explicit constraints that influence downstream decisions and child plans. 4. **Unified correction mechanism**: Since the prompt, invariants, and all other decisions are part of the same tree, correcting any of them uses the same `agents plan correct` command.

Plan: 01KH29QDEE6DZTXKWNKCV8VP0F
├── [prompt_definition] "Increase test coverage to 85% for the whole project."
├── [invariant_enforced] "Prioritize all functionality related to financial transactions and user management"
├── [invariant_enforced] "All API calls over TCP must be mocked"
├── [strategy_choice] "Prioritize auth and payment modules, implement them in parallel before the rest"
├── [subplan_parallel_spawn] "Implement auth and payment modules, in parallel"
│   ├── [subplan_spawn] "Write tests for auth module"
│   │   └── Plan: 01KH29R8WPKPBHRY7Q0NA9XW86
│   │       ├── [prompt_definition] "Write unit tests for auth module using mocks for the remote API calls"
│   │       ├── [implementation_choice] "Test login flow first"
│   │       └── ...
│   └── [subplan_spawn] "Write tests for payment module"
│       └── Plan: 01KH29RN2YKSXMTBDG82AKRHRA
│           ├── [prompt_definition] "Write unit tests for payment module using"
│           └── ...
└── [subplan_parallel_spawn] "Write tests for all modules except the auth and payment modules"
    └──  ...
##### Correcting Decisions (Including Prompts) All corrections use the same unified command:

agents plan correct <decision_id> --mode=<mode> --guidance "<corrected decision text>"
**Parameters:** * ``: The ULID of the decision to correct * `--mode`: Either `revert` (rollback and re-run) or `append` (add fix at end) * `--guidance`: Free-form text specifying what the correct decision should be **Examples:**

# Correct a strategy choice
agents plan correct 01ARZ3NDEKTSV4RRFFQ69G5FAV --mode=revert \
  --guidance "Prioritize the payment module first, not auth, due to upcoming deadline"

# Correct the root prompt to be more specific
agents plan tree <plan_id>
# Shows: [prompt_definition] id=01ARZ3NDEKTSV4RRFFQ69G5FAV "Increase test coverage to 85%"

agents plan correct 01ARZ3NDEKTSV4RRFFQ69G5FAV --mode=revert \
  --guidance "Increase test coverage to 85%, prioritizing auth and payment modules. Use mocks for database tests, not integration tests."

# Correct a subplan's prompt (originally created by parent plan)
agents plan correct 01BRZ4PDFLUTW5SSGR70H6GBW --mode=revert \
  --guidance "Write unit tests for auth module, focusing on edge cases for token expiration"

# Append a fix rather than rewriting history
agents plan correct 01ARZ3NDEKTSV4RRFFQ69G5FAV --mode=append \
  --guidance "The previous approach missed error handling tests - add comprehensive error path coverage"

# Remove an invariant that shouldn't apply
agents plan correct 01CRZ5QEHMVUX6TTHR81I7HCX --mode=revert \
  --guidance "Remove this invariant - TCP mocking is not needed for this module since it has no network calls"

# Add a missing invariant to the plan
agents invariant add --plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J "All database queries must use parameterized statements"
**Note:** CLI commands should not require interactive input. The `--guidance` parameter provides the correction inline. **When to correct the prompt vs. a specific decision:** | Situation | Correction Approach | |-----------|---------------------| | Original request was too vague | Correct the `prompt_definition` decision | | Strategy actor made a bad choice on a specific question | Correct that specific decision | | Parent plan gave a child plan a bad prompt | Correct the child plan's `prompt_definition` | | An invariant should not apply to this plan | Correct (remove) the `invariant_enforced` decision | | A missing constraint should be added | Add a new `invariant_enforced` decision via `agents invariant add --plan` or correct the plan's strategy | | Sequential child plans should run in parallel | Correct the relevant decisions to use a `subplan_parallel_spawn` grouping | Because the prompt is part of the decision tree, the system automatically knows that correcting it invalidates all downstream decisions in that plan (and its child plans). **Decisions are only created during the Strategize phase.** The decision tree captures what choices were made and why, enabling correction and replay. ##### Decision Record Structure

Decision:
  # Identity
  decision_id: ULID # Unique identifier
  plan_id: ULID # Parent plan this decision belongs to
  parent_decision_id: ULID | null # Parent decision (for tree structure)
  sequence_number: int # Order within the plan's decisions
  
  # Classification
  decision_type: enum
    - prompt_definition         # The prompt/description for this plan (root decision)
    - invariant_enforced        # An invariant (from global, project, action, or plan scope) applicable to this plan, added as a constraint
    - strategy_choice           # High-level approach decision during Strategize
    - implementation_choice     # How to implement a specific task
    - resource_selection        # Which resources to read/modify
    - subplan_spawn             # Decision to create a child plan (spawned later in Execute)
    - subplan_parallel_spawn    # Decision to spawn a group of child plans in parallel (contains subplan_spawn children)
    - tool_invocation           # Which skill/tool to use
    - error_recovery            # How to handle a failure
    - validation_response       # Response to validation failure
    - user_intervention         # User provided guidance/correction
  
  # The Decision Itself
  question: str # What question was being answered
  chosen_option: str # What was decided
  alternatives_considered: list[str] # Other options that were evaluated
  confidence_score: float | null # 0.0-1.0 if the actor provided confidence
  
  # Context Snapshot (for replay)
  context_snapshot:
    hot_context_hash: str # Cryptographic hash of the exact context
    hot_context_ref: str # Pointer to the full stored snapshot
    relevant_resources: list[ResourceRef] # Every file/symbol that influenced this decision
    actor_state_ref: str # Complete LangGraph checkpoint
    
  # When the system decides "refactor the authentication module to use async patterns," 
  # it permanently records:
  # - Which files were examined to make that decision
  # - What symbols and dependencies were traced
  # - The exact code state that was analyzed
  # - The reasoning chain that led to this choice
  # - Alternative approaches that were considered but rejected
  
  # Rationale
  rationale: str # Why this option was chosen
  actor_reasoning: str | null # Raw LLM reasoning if available
  
  # Downstream Impact (populated during Execute phase)
  downstream_decision_ids: list[ULID] # Decisions that depend on this one
  downstream_plan_ids: list[ULID] # Child plans spawned because of this decision
  artifacts_produced: list[ArtifactRef] # Files/outputs created under this decision
  
  # Timestamps
  created_at: datetime
  
  # Correction Metadata
  is_correction: bool # Was this decision a correction of another?
  corrects_decision_id: ULID | null # If correction, which decision was replaced
  correction_reason: str | null # Why the correction was made
  superseded_by: ULID | null # If this decision was later corrected
##### Decision Timing | Phase | Decision Activity | |-------|-------------------| | **Strategize** | Decisions are **created**, including `invariant_enforced` decisions for applicable invariants, `subplan_spawn` decisions, and `subplan_parallel_spawn` decisions grouping parallel child plans. `downstream_plan_ids` is empty. | | **Execute** | Child plans are **spawned**. `downstream_plan_ids` is **populated** when child plans are created based on `subplan_spawn` decisions (both standalone and those within `subplan_parallel_spawn` groups). | | **Apply** | No new decisions. History can be flagged for cleanup after successful apply. | ##### Decision Tree Storage Schema

-- Core decision table
CREATE TABLE decisions (
    decision_id TEXT PRIMARY KEY,      -- ULID
    plan_id TEXT NOT NULL,
    parent_decision_id TEXT,
    sequence_number INTEGER NOT NULL,
    decision_type TEXT NOT NULL,        -- prompt_definition, invariant_enforced, strategy_choice,
                                       -- implementation_choice, resource_selection, subplan_spawn,
                                       -- subplan_parallel_spawn, tool_invocation, error_recovery,
                                       -- validation_response, user_intervention
    question TEXT,
    chosen_option TEXT NOT NULL,
    alternatives_considered TEXT,      -- JSON array
    confidence_score REAL,
    rationale TEXT,
    actor_reasoning TEXT,
    context_snapshot TEXT NOT NULL,    -- JSON blob
    is_correction BOOLEAN DEFAULT FALSE,
    corrects_decision_id TEXT,
    correction_reason TEXT,
    superseded_by TEXT,
    created_at TEXT NOT NULL,
    
    FOREIGN KEY (plan_id) REFERENCES plans(plan_id),
    FOREIGN KEY (parent_decision_id) REFERENCES decisions(decision_id),
    FOREIGN KEY (corrects_decision_id) REFERENCES decisions(decision_id),
    FOREIGN KEY (superseded_by) REFERENCES decisions(decision_id)
);

-- Downstream relationships (many-to-many for DAG)
CREATE TABLE decision_dependencies (
    upstream_decision_id TEXT NOT NULL,
    downstream_decision_id TEXT NOT NULL,
    dependency_type TEXT NOT NULL,     -- 'decision', 'plan', 'artifact'
    downstream_ref TEXT NOT NULL,      -- The actual ID of decision/plan/artifact
    
    PRIMARY KEY (upstream_decision_id, downstream_decision_id, downstream_ref),
    FOREIGN KEY (upstream_decision_id) REFERENCES decisions(decision_id)
);

-- Correction history
CREATE TABLE correction_attempts (
    attempt_id TEXT PRIMARY KEY,       -- ULID
    plan_id TEXT NOT NULL,
    original_decision_id TEXT NOT NULL,
    new_decision_id TEXT,
    original_subtree_snapshot TEXT,    -- Reference to archived state
    correction_reason TEXT,
    status TEXT NOT NULL,              -- 'pending', 'executing', 'completed', 'failed'
    created_at TEXT NOT NULL,
    completed_at TEXT,
    
    FOREIGN KEY (plan_id) REFERENCES plans(plan_id),
    FOREIGN KEY (original_decision_id) REFERENCES decisions(decision_id),
    FOREIGN KEY (new_decision_id) REFERENCES decisions(decision_id)
);
### Action #### What an Action Is An **action** is a reusable plan template that is not associated with any projects yet. **Actions are created via CLI commands with a required `--config` YAML file** that fully defines the action. Any CLI options supplied alongside the config file act as optional overrides for values in the YAML. Examples: * "Increase test coverage to 80%" * "Refactor module X to be async-safe" * "Write an RFC for feature Y" * "Provision an infra cluster and validate access" (non-code) Actions are **intentionally project-agnostic** so they can be reused across projects. An action is the first stage of a plan — before it is used. Because of this, **invariants can be attached to actions**. When an action is used (via `agents plan use`), any invariants attached to the action are carried forward as plan-level invariants on the resulting plan. This allows teams to bake constraints directly into reusable templates. Invariants can also be added to a plan after the action is used, via `agents invariant add --plan` or `--invariant` flags on `agents plan use`. #### Action Creation (CLI) Actions are created using the CLI. There are two modes of creation: **1. Config-only (all values from YAML):**

agents action create "local/code-coverage" \
  --config ./actions/code-coverage.yaml
**2. Config with CLI overrides:**

agents action create "local/code-coverage" \
  --config ./actions/code-coverage.yaml \
  --execution-actor "local/fast-executor" \
  --available
In both modes, the YAML file provides the full action definition. Any CLI options provided alongside `--config` override the corresponding values from the file. **Required parameters:** * `--config`: YAML configuration file that fully defines the action (strategy-actor, execution-actor, definition-of-done, etc.) **Optional parameters (overrides):** * `[]`: Namespaced name (positional, e.g., `local/code-coverage`, `myorg/deploy-action`). When provided, overrides the name in the config file. * `--strategy-actor`: Override the Strategize actor from config * `--execution-actor`: Override the Execution actor from config * `--definition-of-done`: Override the completion criteria from config **Optional parameters:** * `--description`: Human-readable description * `--arg`: Argument definitions (can be repeated). Format: `name:type:required|optional:description` * `--invariant`: Invariant to attach to this action (can be repeated). These are carried forward as plan-level invariants when the action is used. * `--invariant-actor`: Invariant Reconciliation Actor for plans created from this action. Can be overridden at use time via `agents plan use --invariant-actor`. * `--reusable`: Whether action remains available after use (default: true) * `--read-only`: Whether action only performs read operations (default: false) Arguments defined with `--arg` are values that will be: * Injected into the description and/or definition of done (via templating) * Passed into the context of the actors * Required when using the action on projects #### Action Data Model (Expanded) A plan in the Action phase has: ##### 1) `name` (namespaced) Format: * `[server:][namespace/]` Rules: * If server is omitted, default server is assumed unless namespace is `local`. * If namespace is omitted, default is `local`. * Names should be stable identifiers (kebab-case recommended). Examples: * `local/code-coverage` * `myusername/code-coverage` * `myorgname/code-coverage` * `prod:myorgname/code-coverage` (server-qualified) ##### 2) `short_description` Optional at creation; auto-filled if blank. ##### 3) `long_description` Optional but recommended for reusable actions. ##### 4) `definition_of_done` (DoD) Required. Must be explicit and testable. ##### 5) `actors` Two actors minimum, two optional: * **strategy_actor** (planner/architect) — required * **execution_actor** (builder/implementer) — required * **estimation_actor** (cost/risk estimator) — optional * **invariant_actor** (Invariant Reconciliation Actor) — optional; resolves invariant conflicts when the plan enters Strategize. Lookup falls back to project, then global config. Actors can be: * an LLM agent (built-in), * a graph (custom yaml, or built-in), Note: graphs are hierarchical allowing them to reference other actors as nodes. Actor abstraction is central: an actor may be a single agent or an entire graph. ##### 6) `reusable` (boolean) * Default: `true`. * If `true`: using the action creates a *new* plan in Strategize while leaving the action available. * If `false`: action self-deletes (or auto-archives) after first use. ##### 7) `read_only` (boolean) * Default: `false`. * If `true`: the plan must only use read-only tools (tools with `read_only: true` in their capability metadata) and must never modify resources (even in sandbox). * Read-only actions are still useful for "investigation reports," architecture reviews, or dry-run planning. ##### 8) `inputs_schema` (recommended addition) To make actions genuinely reusable, actions should declare their inputs: * required args (e.g., target coverage percent), * optional args (e.g., test framework), * validation rules (types, bounds). Example: * `target_coverage_percent`: integer 0–100 ##### 9) `automation_profile` The resolved automation profile name for this plan (e.g., `trusted`, `autonomous`, `local/careful-auto`). Determined at `plan use` time using the profile precedence rules (plan > action > project > global). Once set, it is locked to the plan. ### Strategy (Strategize Phase) #### Using an Action (Transition to Strategize) The `use` command transitions an Action into the Strategize phase by applying it to one or more projects:

# Basic usage
agents plan use local/code-coverage my-api-service

# Multiple projects
agents plan use local/schema-update \
  api-service \
  web-frontend \
  mobile-app

# With action arguments
agents plan use local/code-coverage \
  my-api-service \
  --arg target_coverage_percent=85 \
  --arg test_framework=pytest

# With explicit automation profile
agents plan use local/deploy-action \
  staging-env \
  --automation-profile manual

# With invariants attached at use time
agents plan use local/code-coverage \
  my-api-service \
  --arg target_coverage_percent=85 \
  --invariant "All API calls over TCP must be mocked" \
  --invariant "Do not modify the payments module"
**Parameters:** * ``: Project to apply the action to (positional, can be repeated for multi-project plans) * `--arg`: Action argument values (format: `name=value`) * `--automation-profile`: Override automation profile for this plan * `--invariant`: Invariant to attach to the created plan (can be repeated). These are added as plan-level invariants in addition to any inherited from the action, project, or global scope. When the action is used: 1. A new plan is created with a unique ULID 2. The plan's automation profile is resolved (plan > action > project > global precedence) 3. Any invariants from the action are carried forward as plan-level invariants, combined with any `--invariant` flags provided 4. The plan enters the **Strategize** phase 5. The **Invariant Reconciliation Actor** computes the effective invariant view (resolving conflicts using plan > project > global precedence) 6. The `strategy_actor` begins analyzing the project(s) #### What Strategize Does When an action is **used** on projects, it becomes a **plan in Strategize**. Strategize is: * **read-only**, producing a plan of attack, * responsible for gathering context from project resources, * responsible for collecting applicable invariants (from global, project, action, and plan scopes), computing the effective invariant view via the **Invariant Reconciliation Actor** (applying plan > project > global precedence), and recording them as `invariant_enforced` decisions, * responsible for generating a strategy and child plan blueprint (using `subplan_spawn` and `subplan_parallel_spawn` decisions), * not allowed to execute child plans or modify resources. This "architect vs coder" separation is explicitly described as a core motivation. **Resource-aware dependency analysis**: During the Strategize phase, the strategy actor employs specialized mechanisms to compute precise dependency closures:

# Pseudocode of what happens inside a strategy actor
def compute_closure_for_refactoring(target_module):
    closure = ResourceClosure()
    
    # Direct file dependencies
    closure.add_files(find_imports(target_module))
    closure.add_files(find_includes(target_module))
    
    # Symbol dependencies
    for symbol in extract_exported_symbols(target_module):
        closure.add_files(find_symbol_usage(symbol, scope='project'))
    
    # Test dependencies
    closure.add_files(find_tests_for_module(target_module))
    
    # Build system dependencies
    closure.add_files(find_build_references(target_module))
    
    return closure
The system leverages several key insights: - **Modular boundaries exist**: Even in legacy codebases, there are natural boundaries - **Changes are incremental**: We don't convert 50,000 files atomically - **Dependencies are sparse**: Most modules depend on a small fraction of the codebase - **Interfaces are narrow**: Public APIs are much smaller than implementations #### Strategize Data Model A plan in Strategize contains all Action fields plus: ##### 1) `projects` A list of projects the plan is used on. Important: A strategy plan may target **multiple projects**. Multi-project work in one "window" is considered a major usability advantage over tools that require being run from a single directory. ##### 2) `strategy_context` A structured object describing: * what resources were considered, * how they were retrieved, * what filtering/limits were applied, * what the actor saw. This matters because a plan must be debuggable and correctable later. Recommended fields: * `resource_refs`: IDs of resources used * `queries`: search queries performed * `selected_chunks`: chunk IDs + sources + reasons * `constraints`: context window limits, file ignore patterns * `generated_summaries`: if summarization occurred ##### 3) `strategy` The output plan: * steps (ordered and/or DAG), * conditions/branches ("if tests fail, do X"), * child plans to spawn (including which action templates to use, and whether they should run in parallel via `subplan_parallel_spawn`), * evaluation criteria (how to know success), * risk assessment. ##### 4) `execution_blueprint` (recommended addition) Strategize should output not only narrative text but also a machine-usable blueprint: * list of tasks, * required skills, * expected outputs, * dependencies between tasks. This blueprint becomes the input to Execute. ##### 5) `cost_estimate` and `risk_estimate` (optional) **Cost and risk estimation is optional** but recommended for production use. When enabled, a specialized **estimation actor** analyzes: * The initial prompt/request * The strategy produced by the Strategize phase * Historical data from similar plans (if available) And produces estimates for: * LLM tokens/cost range * Number of steps/child plans expected * Expected risk of rollbacks * Estimated execution time **Implementation**: Similar to how there's a `strategy_actor` and `execution_actor` for each action, there can be an optional `estimation_actor` whose entire job is cost/risk estimation. This actor runs after Strategize completes (before Execute) and its output is informational only.

# Example: Action with estimation actor (estimation-actor overrides config)
agents action create --config ./actions/expensive-refactor.yaml \
  --estimation-actor "local/cost-estimator" \
  "local/expensive-refactor"
This becomes critical in server/multi-user usage and cost controls. ### Execution (Execute Phase) #### What Execute Does Execute is where the plan actually performs work, but **in a sandboxed environment** that can later be reviewed and applied. Key properties: 1. **Work happens in a sandbox** All file modifications, generated artifacts, and intermediate outputs live in an isolated "execution workspace" until Apply. 2. **Execute may spawn child plans** Child plan spawning is a first-class behavior of Execute: a parent plan can distribute work to child plans (sequentially via `subplan_spawn` or concurrently via `subplan_parallel_spawn`) and merge results. 3. **Execute must support checkpointing / rollback (when enabled)** Checkpointable tools allow rolling back to a checkpoint ID to recover from partial failure or wrong turns. 4. **Execute produces a "reviewable diff"** Diff review sandbox is described as a differentiating feature: users can inspect changes before applying. #### Execution Workspace / Sandbox Model (Detailed) A sandbox isolates plan execution from the real project resources until Apply. ##### Key Sandbox Principles 1. **Lazy Sandboxing**: Resources are sandboxed **only when accessed**, not upfront. - A project may have many resources (git repo + 10 databases + cloud accounts) - A plan may only modify one resource - Only accessed resources are sandboxed - Efficient for large projects 2. **Per-Plan Sandboxes**: Each plan and child plan has its own sandbox containing only the resources it edits. 3. **Resource-Defined Strategy**: The sandbox strategy is defined on each **resource**, not by skills or globally. 4. **Cleanup Behavior**: - Sandboxes are cleaned up before application exit when possible - Abandoned sandboxes (from crashes, etc.) are cleaned up on next application run - Completed plan sandboxes are cleaned or archived based on retention policy ##### Sandbox Implementation Strategies Different resource types require different sandbox strategies: | Resource Type | Strategy | Rollback Mechanism | |--------------|----------|-------------------| | `git-checkout` | `git_worktree` | Git reset/checkout | | `git` | `none` | N/A (represents a repo instance — not directly sandboxable) | | `fs-mount` | `copy_on_write` or `overlay` | Restore from snapshot | | `fs-directory` | `copy_on_write` | Restore from snapshot | | Custom database types | `transaction_rollback` | Transaction rollback | | Custom API types | `none` | Often not sandboxable | **1. Git worktree / branch sandbox (preferred for code)** * Create a worktree or temporary branch * All modifications are commits or staged changes * Apply merges/cherry-picks Pros: natural rollback, diff support, efficient Cons: requires git **2. Filesystem copy sandbox** * Copy project directory to a sandbox directory * Execute modifies sandbox copy * Apply syncs diff back Pros: simple Cons: expensive for huge repos **3. Overlay filesystem sandbox** * Use overlayfs-style "copy-on-write" to avoid full copies Pros: efficient Cons: more complex, OS-dependent **4. Transaction-based sandbox (for databases)** * Begin transaction at sandbox creation * All operations within transaction * Rollback on failure, commit on apply Pros: native to databases Cons: long-running transactions can cause issues **5. No sandbox (for non-sandboxable resources)** * Some resources cannot be sandboxed (certain APIs, cloud services) * User proceeds at their own risk * Plan should warn about non-sandboxable resources ##### Multi-Resource Sandboxing When a plan accesses multiple resources: * Each resource gets its own sandbox (based on its defined strategy) * Sandboxes are independent * Apply commits each sandbox separately * If any sandbox Apply fails, others may still succeed (partial apply) **Complete isolation during execution prevents compound errors**: Each plan executes in its own sandbox, which means:

Plan A (refactoring auth module):
- Sandbox A1: Contains only auth/*.cpp, auth_tests/*.cpp
- Cannot see Plan B's intermediate states
- Cannot accidentally depend on Plan B's half-done work

Plan B (updating API endpoints):
- Sandbox B1: Contains only api/*.cpp, api_tests/*.cpp  
- Makes changes assuming current auth interface
- Protected from Plan A's intermediate refactoring
**Hierarchical merge resolution**: When child plans complete, the parent plan performs intelligent merging:

def merge_subplan_results(subplan_results):
    # Group by resource type
    by_resource = group_by_resource_type(subplan_results)
    
    # Apply resource-specific merge strategies
    for resource_type, changes in by_resource:
        if resource_type == 'git-checkout':
            merge_git_changes(changes)  # Three-way merge
        elif resource_type == 'fs-mount':
            merge_fs_changes(changes)   # Copy-on-write reconciliation
        elif resource_type.startswith('database'):
            merge_db_changes(changes)   # Sequential application
    
    # Validate merged state
    run_integration_tests()
#### Execution Data Model A plan in Execute contains: ##### 1) `execution_context` The context used for execution (often smaller/more tactical than strategy context). ##### 2) `execution_log` Structured timeline of: * tool calls (with parent skill noted), * actor calls, * outputs, * errors and retries, * checkpoints created. This log is essential for debugging. ##### 3) `artifacts` Outputs produced: * changed files, * generated files, * reports, * diagrams, * test outputs, * diffs. ##### 4) `sandbox_ref` Pointer to the sandbox location/state: * path, branch name, workspace ID, container ID, etc. ##### 5) `checkpoint_graph` (if enabled) A record of checkpoints: * checkpoint ID * timestamp * tool responsible (and its parent skill) * resources affected * rollback instructions / metadata #### Checkpointing in Execute (Core Safety Mechanism) The intended user-level behavior is: * "Give me a checkpoint ID." * Perform additional operations. * "Roll back to checkpoint X." Not all tools can support this; checkpointing must be declared per tool (in its capability metadata). ##### Tool-level checkpointability Each tool declares (via its capability metadata): * `checkpointable: true|false` * `checkpoint_scope`: what granularity of rollback is supported (file, transaction, commit, snapshot) * `rollback_mechanism`: how rollback occurs Examples: * File tools (from a file-ops skill): snapshot file states pre-modification * Git tools (from a git-ops skill): create commit or stash; rollback is reset/checkout * Shell/CLI tools (running inside a container): rollback by restoring filesystem snapshot or reloading base image state It should be noted that checkpointing is easier when tool scope is constrained (e.g., "only files within a docker image + git"). ##### Plan-level rollback policy Plans should have an option: * `rollback_enabled: true|false` If disabled, the plan may use more generic/unsafe tools with fewer restrictions (useful for low-stakes tasks). #### Execution as Transactions (Recommended) Execution should be treated like a transactional pipeline: * Each step either: * commits a checkpoint on success, or * rolls back to the previous checkpoint on failure. This is explicitly motivated by "partial failure leaves codebase inconsistent" and the need for transaction rollback. #### Tool-Based Resource Modification (Modern Architecture) **IMPORTANT: CleverAgents does NOT parse LLM output to extract code.** Instead, it uses the modern tool-based approach pioneered by Claude Code, Cursor, and Aider where: 1. **LLMs call tools directly** (`edit_file()`, `write_file()`, `delete_file()`, etc.) — tools provided by referenced skills 2. **Tools operate on the sandbox** - each tool invocation modifies sandbox state directly 3. **ChangeSet is built from tool invocations** - not by parsing LLM text output 4. **Validation runs on sandbox state** - after tools execute, not on parsed output This architecture provides: * **Atomic operations**: Each tool call is a discrete, trackable change * **No parsing ambiguity**: Tools have structured parameters (path, content, etc.) * **Resource-agnostic**: Same pattern works for files, databases, APIs, any resource type * **Safety by design**: Tools run in sandbox with defined capabilities and restrictions * **MCP compatibility**: Tools from MCP-based skills map directly to MCP tools for external integrations ##### How It Works ```kroki-mermaid sequenceDiagram participant LLM as LLM Agent participant Router as Tool Router participant Sandbox as Sandbox participant CS as ChangeSet participant Val as Validator LLM->>Router: Tool call (with parameters) Router->>Router: Validate parameters Router->>Router: Enforce capability restrictions Router->>Sandbox: Execute tool in sandbox Sandbox->>Sandbox: Operate on sandboxed state Sandbox->>Sandbox: Record invocation Sandbox->>Sandbox: Create checkpoint (if needed) Sandbox->>CS: Emit Change record CS->>CS: Accumulate into ChangeSet CS->>Val: Submit for validation Val->>Val: Run validators on sandbox state Val->>Val: Generate diff from ChangeSet Val-->>LLM: Present for review before Apply ``` ##### Built-in Resource Tools CleverAgents provides these core tools (via built-in skills) for resource manipulation: | Tool | Description | Creates Change? | |-------|-------------|-----------------| | `read_file(path)` | Read file contents | No | | `write_file(path, content)` | Create/overwrite file | Yes | | `edit_file(path, changes)` | Apply targeted edits | Yes | | `delete_file(path)` | Remove file | Yes | | `move_file(src, dst)` | Rename/move file | Yes | | `create_directory(path)` | Create directory | Yes | | `list_files(pattern)` | List files matching glob | No | | `search_files(pattern, content)` | Search file contents | No | | `get_file_info(path)` | Get file metadata | No | Each built-in tool automatically: * Operates within sandbox boundaries * Records changes to the ChangeSet * Validates parameters against project configuration * Enforces deny-list patterns (`.git/`, `node_modules/`, etc.) ##### Why Not Parse LLM Output? The obsolete approach of parsing markdown code fences has fundamental problems: 1. **Ambiguity**: Is text explanation or code? Where does one file end and another begin? 2. **Fragility**: Models output varying formats; regex parsing is brittle 3. **Loss of semantics**: You lose the intent (create vs modify vs delete) 4. **No atomicity**: Can't rollback individual operations 5. **Resource-limited**: Only works for files, not databases or other resources The tool-based approach solves all of these by making each operation explicit, typed, and trackable. ### Semantic Error Prevention CleverAgents provides multiple layers of proactive error prevention that catch semantic errors before they can propagate through the system. #### Layer 1: Decision-time Validation During Strategize Every decision includes semantic validation:

Decision: Refactor payment module to async
alternatives_considered:
  - "Convert to async/await patterns" (chosen)
  - "Use thread pool with channels" (rejected: doesn't integrate with async ecosystem)
  - "Keep synchronous with timeout" (rejected: doesn't solve core latency issue)
confidence_score: 0.85
validation_performed:
  - Checked all payment API consumers can handle async
  - Verified database driver supports async operations
  - Confirmed no regulatory requirement for sync processing
#### Layer 2: Execution-time Semantic Guards The execution actor uses a tool node that references the independently registered `local/validate-api-compat` tool:

# Actor graph uses a named tool node for semantic validation
actors:
  code_executor:
    type: graph
    skills:
      - local/semantic-validators    # Skill containing validation tools for LLM tool-calling
    nodes:
      - name: semantic_validator
        type: tool
        tool: local/validate-api-compat # Named tool from Tool Registry
The `local/validate-api-compat` tool is independently registered via its own YAML:

# File: tools/validate-api-compat.yaml
cleveragents:
  version: "3.0"

tool:
  name: local/validate-api-compat
  description: "Check for breaking API changes and attempt auto-migration"
  source: custom

  capability:
    writes: true
    checkpointable: true

  code: |
    # Not just syntax checking - semantic validation
    old_api = extract_api_signature(previous_version)
    new_api = extract_api_signature(current_version)
    
    breaking_changes = find_breaking_changes(old_api, new_api)
    if breaking_changes:
        affected_consumers = find_api_consumers(breaking_changes)
        migration_plan = generate_migration(breaking_changes)
        
        if can_auto_migrate(affected_consumers, migration_plan):
            apply_migration(migration_plan)
        else:
            raise SemanticError(
                "Breaking API changes require manual review",
                changes=breaking_changes,
                affected=affected_consumers
            )
The `local/semantic-validators` skill references this tool by name:

# File: skills/semantic-validators.yaml
skill:
  name: local/semantic-validators
  description: "Semantic validation tools for API compatibility and code invariants"
  tools:
    - local/validate-api-compat
#### Layer 3: Invariant Enforcement Invariants are named constraints that guide and constrain plan execution. They can be attached at four scopes, all managed through the unified `agents invariant` command: * **Global invariants**: Apply to all plans across all projects. Added via `agents invariant add --global`. * **Project invariants**: Apply to all plans targeting a specific project. Added via `agents invariant add --project`. Can also be attached at creation time via `agents project create --invariant`. * **Action invariants**: Attached to an action and carried forward as plan-level invariants when the action is used. Added via `agents invariant add --action`. Can also be attached at creation time via `agents action create --invariant`. * **Plan invariants**: Apply to a specific plan and its child plans. Added via `agents invariant add --plan`. Can also be attached at creation time via `agents plan use --invariant`. **Precedence and conflict resolution**: When invariants from different scopes conflict, narrower scopes override broader scopes: * **Plan-level** invariants override **project-level** and **global-level** invariants. * **Project-level** invariants override **global-level** invariants. Conflict resolution is performed by the **Invariant Reconciliation Actor** — a dedicated actor responsible for comparing invariants across scopes, identifying conflicts, and producing the final **effective invariant view** for a plan. The Invariant Reconciliation Actor is set at three levels via `--invariant-actor`: 1. **Global config**: Set via `agents config set invariant-actor `. Defines the default Invariant Reconciliation Actor used when neither the project nor the plan specifies one. 2. **Project-level**: Set via `--invariant-actor` on `agents project create`. If a project defines an Invariant Reconciliation Actor, it is used to reconcile that project's invariants against global invariants. 3. **Plan-level**: Set via `--invariant-actor` on `agents action create` (carried forward when the action is used) or `agents plan use` (which overrides whatever was set on the action). If a plan has an Invariant Reconciliation Actor, it reconciles invariants from all scopes (plan, project, and global) and produces the final effective view for that plan. The lookup order is: plan → project → global config. The first Invariant Reconciliation Actor found is used. **Invariant view calculation**: When an action is used and a plan enters the Strategize phase, the Invariant Reconciliation Actor computes the effective invariant view by: 1. Collecting all invariants from global, project, plan, and action scopes. 2. Identifying conflicts (invariants from different scopes that contradict each other). 3. Applying precedence rules (plan > project > global) to resolve conflicts. 4. Producing the final set of effective invariants. Each effective invariant is then recorded as an `invariant_enforced` decision in the plan's decision tree. This makes invariants visible, auditable, and correctable through the standard decision correction mechanism. **Child plan inheritance**: When a top-level plan spawns child plans, the parent's effective invariant view (already reconciled) is passed down to each child plan. Child plans do not re-run reconciliation — they inherit the parent's resolved view. **Correcting invariants**: The correction mechanism for `invariant_enforced` decisions supports two operations: * **Remove**: Remove an existing invariant from the plan's decision tree (the invariant remains defined at its scope but is no longer enforced for this plan). * **Add**: Add a new invariant to the plan. When adding, the user can select from invariants already accessible to the plan (those defined at the plan, action, project, or global scope), or provide free-form text to create a new ad-hoc invariant.

# Add invariants at different scopes
agents invariant add --global "All public APIs must maintain backward compatibility"
agents invariant add --global "Payment processing must be idempotent"
agents invariant add --project local/api-service \
  "Database transactions must complete within 5 seconds"
agents invariant add --project local/api-service \
  "Authentication must always use OAuth2"
agents invariant add --plan <PLAN_ID> "All API calls over TCP must be mocked"
agents invariant add --action local/code-coverage "Test files must not import production secrets"

# List invariants
agents invariant list --global
agents invariant list --project local/api-service
agents invariant list --plan <PLAN_ID> --effective   # Shows reconciled view

# Remove an invariant
agents invariant remove <INVARIANT_ID>

# Attach invariants at creation time (convenience)
agents project create --invariant "All endpoints must validate auth tokens" local/api-service
agents action create --config ./actions/code-coverage.yaml --invariant "Test files must not import production secrets" local/code-coverage
agents plan use local/code-coverage local/api-service --invariant "Mock all network calls"

# Correct an invariant decision (remove or replace via standard correction)
agents plan correct <DECISION_ID> --mode=revert \
  --guidance "Remove this invariant - it does not apply to this module"
The system collects, reconciles, and checks invariants:

class InvariantEnforcer:
    def compute_effective_invariants(self, plan):
        """Compute the effective invariant view for a plan using the Invariant Reconciliation Actor."""
        # 1. Collect raw invariants from all scopes
        raw = self.collect_all_invariants(plan)
        
        # 2. Find the Invariant Reconciliation Actor (plan -> project -> global config)
        reconciler = (
            self.get_plan_invariant_actor(plan)
            or self.get_project_invariant_actor(plan)
            or self.get_global_invariant_actor()
        )
        
        # 3. Reconcile: apply precedence (plan > project > global), resolve conflicts
        effective = reconciler.reconcile(raw, precedence=['plan', 'project', 'global'])
        return effective
    
    def collect_all_invariants(self, plan):
        """Collect invariants from all scopes accessible to this plan."""
        invariants = []
        invariants.extend(self.get_global_invariants())
        for project in plan.projects:
            invariants.extend(self.get_project_invariants(project))
        invariants.extend(self.get_action_invariants(plan.action))
        invariants.extend(self.get_plan_invariants(plan))
        return invariants
    
    def check_invariant_preservation(self, changes, enforced_invariants):
        """Check that changes respect all enforced invariants."""
        for invariant in enforced_invariants:
            if not self.verify_invariant(invariant, changes):
                return InvariantViolation(invariant, changes)
        return Success()
#### Layer 4: Predictive Error Prevention The system learns from past failures:

Error Pattern Database:
  - pattern: "Async conversion in payment module"
    historical_failures:
      - "Race condition in payment confirmation"
      - "Timeout handling breaks idempotency"
    preventive_checks:
      - "Add explicit transaction boundaries"
      - "Verify idempotency keys are preserved"
      - "Check distributed lock acquisition"
### Applied (Apply Phase) #### What Apply Does Apply takes the sandboxed work product and makes it "real" in the project. Core properties: 1. **Apply is a controlled commit step** Apply exists specifically to separate "generated work" from "committed work," enabling review and safer automation. 2. **Apply is often the highest-risk step** It changes real systems. This is where permissions, approvals, and checks matter most. 3. **Apply produces a terminal 'applied' plan** After successful apply, the plan becomes Applied. #### Apply Responsibilities (Recommended Checklist) Apply should perform (configurable) validations before committing: * **Diff review gate** * If `auto_apply` is `false` in the automation profile, show: * changed files summary, * full diff, * risk warnings. * **Pre-apply tests** * Run validation as defined by the actor and/or project (see below). * **Conflict resolution** * If applying to a git repo, handle rebase/merge conflicts safely. * **Audit log** * Record who applied, what changed, when, and why. #### Validation Configuration Validation is **defined by the actor configuration and project settings**, not hardcoded. **Actor-defined validation**: The execution actor's workflow should include validation nodes that: 1. Read project-defined validations from context 2. Execute each validation command and collect results 3. For **required** validations that fail: attempt to fix the issue within the bounds of the strategy, retrying up to the configured limit. If the actor determines it cannot solve the problem within the restrictions given by the strategy phase, it may suggest a change to the decision tree and cause the strategy to be recalculated (whether this happens automatically depends on the automation profile). 4. For **informational** validations that fail: record the result in the plan summary without blocking execution. **Project-defined validation**: Each project defines its validations via `agents project validation add`:

# Add required validations
agents project validation add --description "Run unit tests with coverage" \
  --required --resource repo local/api-service "pytest --cov=src --cov-fail-under=80"
agents project validation add --description "Lint check" \
  --required local/api-service "ruff check ."
agents project validation add --description "Type checking" \
  --required local/api-service "pyright"

# Add informational (non-blocking) validations
agents project validation add --description "Check bundle size (advisory)" \
  --informational local/api-service "node scripts/check-bundle-size.js"
This information is passed into the actor's context, allowing generic actors (not specific to any project) to execute appropriate validation. Each validation is run independently, and results are collected into the plan's validation summary. #### Validation Failure Handling When a **required** validation fails during Execute: 1. **Self-fix**: The execution actor attempts to fix the issue within the bounds of the strategy (e.g., fix failing tests, correct lint errors). The actor iterates, re-running the validation after each fix attempt. 2. **Retry limit**: After the configured number of failed attempts, self-fix stops. 3. **Strategy recalculation**: If the actor determines it cannot solve the problem within the restrictions given by the strategy phase, it may request a change to the decision tree. This causes the Strategize phase to be re-run for the affected subtree. Whether this happens automatically or requires user approval depends on the automation profile (the `auto_strategy_revision` flag). 4. **User intervention**: If the automation profile requires approval, or if strategy recalculation also fails, the system pauses and requests user guidance. 5. **Resume**: Plan continues with new guidance after user input. When an **informational** validation fails, the result is recorded in the plan's validation summary but execution continues normally. The user can prompt the plan with additional instructions when stuck:

agents plan prompt <plan_id> "Try using mock objects for the database tests"
#### Apply Data Model A plan in Apply includes: * `apply_summary` * `applied_artifacts` (final commit hash, merged PR link, file list) * `final_validation_results` (per-validation command outputs and pass/fail status) * `approval_record` (if human approvals are required) * `deployment_record` (optional, if apply triggers deploy) #### "Applied" Terminal State When Apply succeeds: * plan.phase = `applied` * plan.state = `complete` * the sandbox may be cleaned up or archived depending on retention policy When Apply fails: * plan.phase remains Apply * plan.state = `errored` * sandbox remains intact for inspection/retry ### Project A **project** is the boundary that answers: * "Where is the work happening?" * "What can this plan read and write?" * "What skills (and their tools) can this plan use?" * "What context is available?" A project is a **collection of linked resources and configuration**. Projects link to independently registered resources from the **Resource Registry** — they do not define resources inline. A resource can be linked to multiple projects, enabling shared resources across teams and workflows. **Important: Projects are created via CLI commands, NOT YAML configuration files.** #### Project Types: Local vs Remote Projects are classified based on their resources: | Type | Definition | Where Plans Can Execute | |------|------------|------------------------| | **Local** | Contains at least one local-only resource | Client only | | **Remote** | All resources are remotely accessible | Client or Server | This distinction matters for server mode: the server can only execute plans on **remote** projects because it needs network access to all resources. #### Project Creation (CLI)

# Step 1: Register resources independently
agents resource add git-checkout local/api-repo \
  --path /repos/api-service \
  --branch main

agents resource add local/database local/staging-db \
  --connection-string "postgresql://staging.example.com/mydb" \
  --read-only

# Step 2: Create the project
agents project create "my-api-service"

# Step 3: Link resources to the project
agents project link-resource "my-api-service" local/api-repo

agents project link-resource "my-api-service" local/staging-db --read-only
Resources are registered once and can be linked to multiple projects. The resource's type, sandbox strategy, and capabilities are defined by its resource type in the Resource Registry — not by the project. #### Project Data Model A project includes: ##### 1) Identity * `project_id` (ULID) * `name` * `namespace` (follows same rules as actors: `local/`, `/`, `/`) * `is_remote` (boolean, derived from resources) ##### 2) Linked Resources Resources are the "things you can act on." Projects **link** to independently registered resources from the Resource Registry rather than defining them inline. This means: * A resource can be linked to multiple projects (shared resources). * The resource's type, capabilities, sandbox strategy, and DAG relationships are defined by the resource itself — not by the project. * Projects can apply project-level overrides when linking (e.g., marking a writable resource as read-only within a specific project context). Each linked resource reference has: * `resource_name` (reference to a Resource Registry entry) * `project_read_only` (boolean — project-level read-only override) * `alias` (optional short name for referencing within the project) The full resource details (type, location, sandbox strategy, capabilities, parent/child DAG, etc.) are stored in the Resource Registry. See the **Resources** section for details. ##### 3) Context configuration Project-level defaults: * ignore patterns (like `.gitignore` semantics) * max file size * indexing strategy * preferred chunking/summarization policy (even if evolving) * context retention policy ##### 4) Security / permissions defaults * who can run write plans * which skill categories are allowed or denied * whether apply requires approvals #### Multi-Project Operations A single plan may target multiple projects (e.g., updating shared schemas across services). This is considered a key UX advantage over "run in one directory" systems. Because resources are independently registered and can be linked to multiple projects, shared resources across projects are a natural part of the architecture. In multi-project execution: * Strategize must clarify which steps affect which projects. * Execution must isolate sandboxes per project OR define a composite sandbox. When multiple projects share the same resource, a single sandbox for that resource is used. * Apply must commit changes to each project separately, with separate approval records if necessary. * Tool resource bindings are resolved per-project — the same tool may bind to different resources depending on which project context it runs in. ### Namespaces Namespaces define **ownership, scoping, and discoverability** of actors, tools, skills, resources, resource types, actions, projects, and plans. **All named entities use the format `/`.** #### Namespace Types | Namespace | Scope | Storage | Examples | |-----------|-------|---------|----------| | `local/` | Current machine only | Local database | `local/my-reviewer`, `local/test-action` | | `/` | Personal server namespace | Server database | `freemo/code-analyzer`, `jsmith/deploy-script` | | `/` | Organization namespace | Server database | `cleverthis/standard-review`, `acme/deploy-action` | | `openai/`, `anthropic/`, etc. | Built-in LLM actors | N/A (built-in) | `openai/gpt-4`, `anthropic/claude-3-opus` | #### Namespace Rules * **`local/`** * Reserved namespace for local-only items * Exists only on the current machine * Stored in local database * Fast iteration, no sharing * Default namespace when none specified * **`/`** (e.g., `freemo/`, `jsmith/`) * Personal namespace on the server * Created when user registers an account * Stored on server, synced when connected * Used for reusable entities (actions, actors, tools, skills, resources) a user wants across machines * Only the owning user can create/modify items * **`/`** (e.g., `cleverthis/`, `acme/`) * Organization namespace on the server * Created when organization is registered * Shared across team members * Permissions and approvals managed at org level * All namespaced entities (actions, actors, tools, skills, resources, projects) can be centrally managed * **Built-in Provider Namespaces** (`openai/`, `anthropic/`, `google/`, etc.) * Reserved for built-in LLM actors * Automatically available when API keys are configured * In server mode: available if logged in and server has keys * In local mode: requires environment variables or app configuration * Cannot be used for custom actors #### Server-qualified Names To disambiguate between servers (when connected to multiple): * `dev:freemo/code-coverage` (personal namespace on dev server) * `prod:cleverthis/deploy-action` (org namespace on prod server) This enables a pattern where: * local machine runs a lightweight client * server stores canonical definitions * multiple servers can coexist ### Actor #### What an Actor Is An **actor** is the abstraction that generalizes "agent" into "anything conversational." * It can be as small as a single LLM agent. * It can also be an entire graph that itself calls other actors/tools. * Actors can be nested/hierarchical, enabling "orchestrator of orchestrators." **Every custom actor IS a graph** (a LangGraph defined via YAML configuration). Even a simple actor wrapping a single LLM is technically a graph with one node. #### Actor Naming Actors are **always named using `/` format**: * `local/my-reviewer` - Local actor * `freemo/code-analyzer` - Personal server actor * `cleverthis/deploy-specialist` - Organization actor * `openai/gpt-4` - Built-in LLM actor #### Actor Definition (YAML Configuration) Actors are defined via **YAML configuration files**. Tools and skills are also defined via their own YAML configuration files. YAML configuration is used for actors, tools, and skills (not for actions or projects). Example actor configuration (see `examples/` directory for full examples):

cleveragents:
  version: "3.0"
  default_actor: workflow_controller

actors:
  # Simple LLM actor with skills referenced by name
  my_assistant:
    type: llm
    config:
      actor: openai/gpt-4 # Reference to built-in actor
      temperature: 0.7
      system_prompt: |
        You are a helpful assistant.
        Current task: {{ context.task_description }}
    skills:
      - local/file-ops               # Grants access to all tools in this skill
      - local/git-ops

  # LLM actor with a composite skill (includes many sub-skills)
  data_processor:
    type: llm
    config:
      actor: anthropic/claude-3-opus
      system_prompt: |
        You are a data processing assistant.
    skills:
      - local/data-toolkit           # A skill containing analysis + transformation tools

  # Actor referencing another actor
  reviewer:
    type: llm
    config:
      actor: local/code-reviewer # Reference to another custom actor
      memory_enabled: true
      max_history: 20
    skills:
      - local/file-ops
      - local/git-ops

routes:
  main_workflow:
    type: graph
    entry_point: start
    nodes:
      - name: analyze
        type: agent
        agent: my_assistant
      - name: process
        type: agent
        agent: data_processor
    edges:
      - source: start
        target: analyze
      - source: analyze
        target: process
      - source: process
        target: end

context:
  global:
    task_description: "Default task"
#### Actor Arguments **All actors can receive arguments when invoked**, including built-in actors. Arguments are passed when: 1. An action is used on projects (arguments flow to strategy/execution actors) 2. An actor is directly invoked Arguments are injected into the actor's context and can be used in Jinja2 templates within prompts. For built-in actors (like `openai/gpt-4`), common arguments include: * `temperature` * `max_tokens` * `system_prompt` #### Actor Composition (Hierarchical References) Actors can reference other actors **by name**:

actors:
  complex_workflow:
    type: llm
    config:
      actor: local/base-analyzer # References another actor
**Load order matters**: Referenced actors must be loaded/defined before actors that depend on them. This enables hierarchical composition where: * Actor A's graph can include nodes that call Actor B (by name) * Actor B itself is a graph that might call Actor C * And so on... #### Actor vs Agent (Relationship) * **Agent**: an actor that is specifically an LLM with tools and reasoning behaviors. * **Actor**: may be an agent, but may also be: * a composite workflow, * a multi-step graph, * a wrapper around a third-party system (as long as it's "text in → text out" conversationally). #### Actor Definition Fields (From Notes + Extended) A robust actor schema should include: * `name` (namespaced) * `provider` (LLM provider or runtime target) * `model` * `system_prompt` (or prompt template) * `skill_access_policy` (which skill categories/names are allowed or denied) * `graph_descriptor` (for composite actors) * `memory_policy` (per-plan/per-actor—see memory section) * `context_view_policy` (what context this actor sees) * `limits` (token limits, tool call limits, retries) * `cost_policy` (caps, budgets) * `metadata` (use cases, version) #### Actor Composition and Graphs Actors can reference: * other actors (by namespaced name) * skills (by namespaced name — all tools from referenced skills become available) * subgraphs This is central to enabling both: * multi-agent orchestration, and * modular reuse of workflows. #### Nodes in the Graph: Actors and Tools Graph nodes can be any of: * an **actor** (another LLM agent or composite workflow, referenced by name), * a **tool node** — a deterministic, non-LLM step that directly invokes a tool. Tool nodes can: * **Reference a named registered tool** by its fully-qualified name (e.g., `tool: local/run-migrations`). The tool must be registered in the Tool Registry via `agents tool add`. Metadata can optionally be overridden at the point of use. * **Define an anonymous inline tool** using the same format as a tool YAML body (with `anonymous: true`). This is useful for one-off, workflow-specific operations that don't warrant separate registration. This is a powerful simplification: actors provide intelligence, tools provide capability (both as graph nodes and through skills for LLM tool-calling), and skills organize tools into reusable collections. Everything participates in the same graph. **Tool node with named tool reference:**

nodes:
  - name: run_db_migrate
    type: tool
    tool: local/run-migrations # Named tool from Tool Registry
    override:                           # Optional metadata override
      capability:
        human_approval_required: true

  - name: spawn_tests
    type: tool
    tool: local/create-subplan # Another named tool
**Tool node with anonymous inline tool:**

nodes:
  - name: custom_validation
    type: tool
    anonymous: true
    description: "Validate output format before proceeding"
    input_schema:
      type: object
      properties:
        data: { type: object }
    capability:
      read_only: true
    code: |
      # Inline Python — same format as a named tool YAML body
      if not params["data"].get("status"):
          raise ValueError("Missing status field")
      return {"valid": True}
### Agent #### Agent Definition In CleverAgents, an **agent** is a specialized actor with: * a conversational interface, * tool-calling capability, * potentially memory, planning heuristics, and role identity. Examples of agent roles: * planner/architect (strategy actor) * coder/implementer (execution actor) * reviewer/qa agent * release/apply agent The transcript explicitly discusses role separation like planner/coder/reviewer in context views/memory proposals. #### Agent Behavior Configuration Agents should be configurable without code changes: * prompt templates * tool sets * safety constraints * style constraints (verbosity, code style) * reliability controls (self-checks, validations) A design goal is user empowerment: "users customize LLM behavior without modifying core code." ### Tools #### What a Tool Is A **tool** is a namespaced, independently registered, callable operation. It is the **atomic unit of execution** in CleverAgents — the smallest piece of functionality that can read, write, or transform resources. Tools are defined in their own YAML configuration files, managed through the `agents tool` CLI commands, and registered in the **Tool Registry**. Tools follow the same `/` naming convention as actors, skills, and other entities (e.g., `local/run-migrations`, `cleverthis/validate-api`, `local/create-subplan`). They support optional server-qualified prefixes for multi-server disambiguation (e.g., `dev:freemo/custom-analysis`). #### The Dual Role of Tools Tools serve two distinct roles in CleverAgents: 1. **As components of a Skill**: A skill references tools by name to assemble a reusable capability collection. When an actor references a skill, all of that skill's tools (including those from included child skills) become available to the actor's LLM agent for tool-calling. 2. **As tool nodes in an Actor graph**: An actor's graph definition can include `type: tool` nodes that directly invoke a specific tool. This is used for deterministic, non-LLM steps in a workflow — e.g., spawning a child plan, running validation, or executing a migration. The tool node either references a named registered tool or defines an anonymous inline tool. ```kroki-mermaid block-beta columns 3 space:3 block:header:3 A["Tool: Dual Role"] end space:3 block:role1:1 B["Role 1: In a Skill"] C["Skill: local/devops"] D["tools:"] E[" - local/run-migrations"] F[" - local/validate-schema"] G["(tool-calling by LLM)"] end space:1 block:role2:1 H["Role 2: In an Actor Graph"] I["Actor Graph node:"] J[" name: run_db"] K[" type: tool"] L[" tool: local/run-migrations"] M["(deterministic invoke)"] end ``` #### Tool Configuration (YAML) Tools are defined in their own YAML configuration files, separate from skills and actors. A tool YAML file declares the tool's identity, schema, capability metadata, and implementation:

# File: tools/run-migrations.yaml
cleveragents:
  version: "3.0"

tool:
  name: local/run-migrations
  description: "Run database migrations for the API service"

  source: custom # mcp | agent_skill | builtin | custom

  # Resource bindings — what resources this tool needs access to
  resources:
    db:
      type: local/database
      access: read_write
      required: true
      description: "Target database for migrations"

  input_schema:
    type: object
    properties:
      direction:
        type: string
        enum: [up, down]
      count:
        type: integer
        default: 1
    required: [direction]

  capability:
    writes: true
    write_scope:
      resource_slots: [db] # References the "db" resource slot
    checkpointable: true
    checkpoint_scope: transaction
    side_effects: [schema_mutation]

  code: |
    import subprocess
    direction = params["direction"]
    count = params.get("count", 1)
    db = ctx.resources["db"]          # Access the bound database resource
    result = subprocess.run(
        ["alembic", direction, str(count)],
        capture_output=True, text=True, cwd=db.sandbox.root
    )
    return {"stdout": result.stdout, "returncode": result.returncode}
Another example — a tool that wraps an MCP server endpoint:

# File: tools/create-github-issue.yaml
cleveragents:
  version: "3.0"

tool:
  name: local/create-github-issue
  description: "Create a GitHub issue via MCP"

  source: mcp
  mcp_server:
    command: "npx @anthropic/mcp-github"
    env:
      GITHUB_TOKEN: "${GITHUB_TOKEN}"
    tool_name: create_issue # The tool name as exposed by the MCP server

  capability:
    writes: true
    write_scope: [github:issues]
    checkpointable: false
And an Agent Skill tool:

# File: tools/deploy-staging.yaml
cleveragents:
  version: "3.0"

tool:
  name: local/deploy-staging
  description: "Deploy the current branch to the staging environment"

  source: agent_skill
  agent_skill:
    path: ./skills/deploy-to-staging
    sandbox_policy: container
    allowed_tools: ["Bash(docker:*)", "Bash(kubectl:*)", "Read"]

  capability:
    writes: true
    checkpointable: false
    side_effects: [deploy, infrastructure]
#### Tool Registration and Management Tools are managed through the `agents tool` CLI commands:

# Register a new tool from its YAML configuration
agents tool add --config ./tools/run-migrations.yaml local/run-migrations

# Update an existing tool (re-reads the config file, overwrites registration)
agents tool add --config ./tools/run-migrations.yaml --update local/run-migrations

# List all registered tools
agents tool list

# Show details for a tool (schema, capability, references)
agents tool show local/run-migrations

# Remove a tool
agents tool remove local/run-migrations
Once registered, a tool is available to be referenced by skills (in their `tools` list) and by actor graphs (as `type: tool` nodes). Tools persist in the database (local or server) and follow the same namespace rules as actors and skills. #### Anonymous Tools An **anonymous tool** is an inline tool definition that appears directly in a skill YAML or an actor graph node. Anonymous tools use the **same format** as a named tool's YAML definition (same `input_schema`, `capability`, and `code` fields) but lack a namespaced name. They are: * **Not registered** in the Tool Registry * **Not reusable** — they exist only within the YAML file where they are defined * **Useful for one-off operations** that are too specific to warrant separate registration Anonymous tools in a skill YAML:

skill:
  name: local/my-skill
  tools:
    - local/run-migrations          # Named tool reference
    - local/validate-api-compat     # Named tool reference

  anonymous_tools:                  # Inline definitions, same format as tool YAML
    - description: "One-off data cleanup for this project"
      input_schema:
        type: object
        properties:
          table: { type: string }
      capability:
        writes: true
        checkpointable: true
      code: |
        # ... Python code ...
        return {"cleaned": count}
Anonymous tools in an actor graph node:

nodes:
  - name: custom_step
    type: tool
    anonymous: true
    description: "Inline validation specific to this workflow"
    input_schema:
      type: object
      properties:
        data: { type: object }
    capability:
      read_only: true
    code: |
      # ... Python code ...
      return {"valid": True}
The anonymous tool format is intentionally identical to the body of a named tool YAML — this means promoting an anonymous tool to a named, registered tool is a simple copy-paste into its own YAML file and `agents tool add`. #### Metadata Overrides When referencing a named tool in a skill or actor graph, its registered metadata can optionally be **overridden** at the point of use. This allows context-specific adjustments without modifying the tool's global registration. **Overriding tool metadata in a skill:**

skill:
  name: local/strict-devops
  tools:
    - name: local/run-migrations
      override:
        capability:
          human_approval_required: true # Override: require approval in this skill
          write_scope: [database:staging] # Override: restrict scope for this context

    - local/validate-api-compat            # No overrides, use as registered
**Overriding tool metadata in an actor graph node:**

nodes:
  - name: safe_migrate
    type: tool
    tool: local/run-migrations
    override:
      capability:
        human_approval_required: true
**Overriding tool metadata when including a sub-skill:** When a skill includes another skill (importing all its tools), individual tools from the included skill can have their metadata overridden:

skill:
  name: local/production-ops
  includes:
    - name: local/devops-toolkit
      tool_overrides:
        - tool: local/run-migrations
          override:
            capability:
              human_approval_required: true # In this context, require human approval
              write_scope: [database:production]

        - tool: local/create-github-issue
          override:
            capability:
              required_permissions: [org:write] # Stricter permissions in this context
Override rules: 1. Overrides are **shallow-merged** — only the specified fields are replaced; unspecified fields retain their registered values. 2. Overrides **never persist** back to the Tool Registry — they apply only at the point of use. 3. Built-in tool metadata **cannot be overridden** (it is authoritative from the implementation). 4. The override scope is limited to `capability` and `description` fields. Schema (`input_schema`, `output_schema`) cannot be overridden because it would break callers' expectations. #### Resource Bindings Tools operate on **resources** — git repositories, filesystems, databases, and more. The **resource binding** system declares and resolves the relationship between a tool and the resources it needs access to. ##### Resource Slots A tool declares one or more **resource slots** in its YAML configuration. Each slot is a typed placeholder that specifies: * **Slot name**: A logical name used to reference the resource within the tool's code and parameters. * **Resource type**: The resource type required (e.g., `git`, `fs-mount`, `local/database`). The bound resource must be of this type (or a compatible subtype). * **Access mode**: `read_only`, `write_only`, or `read_write`. * **Description**: Human-readable explanation of what the tool uses this resource for. * **Required/optional**: Whether the slot must be bound for the tool to function. Example tool YAML with resource slots:

tool:
  name: local/run-migrations
  description: "Run database migrations"
  source: custom

  resources:
    db:
      type: local/database
      access: read_write
      required: true
      description: "Target database for migrations"

  capability:
    writes: true
    write_scope: [db:migrations] # References the "db" slot
    checkpointable: true

  code: |
    direction = params["direction"]
    db_resource = ctx.resources["db"]   # Access the bound resource
    result = db_resource.handler.execute_migration(direction, db_resource.sandbox)
    return {"status": "ok"}
A tool that works with multiple resources:

tool:
  name: local/cross-repo-diff
  description: "Compare files across two git repositories"
  source: custom

  resources:
    source_repo:
      type: git-checkout
      access: read_only
      required: true
      description: "Source repository to compare from"
    target_repo:
      type: git-checkout
      access: read_only
      required: true
      description: "Target repository to compare against"

  capability:
    read_only: true

  code: |
    source = ctx.resources["source_repo"]
    target = ctx.resources["target_repo"]
    # ... compare files across repos ...
##### Three Binding Modes Resource slots are resolved to actual resources through one of three binding modes: **1. Contextual Binding (default)** The slot declares a resource type requirement, and the system resolves it from the plan's project context at activation time. This is the most common mode — the tool says "I need a git-checkout resource" and the system finds one among the project's linked resources.

resources:
  repo:
    type: git-checkout
    access: read_write
    # No `bind` field → contextual binding
Resolution rules for contextual binding: * The system searches the plan's project for linked resources matching the slot's type. * If exactly one resource of the right type exists, it is automatically bound. * If multiple resources match, the system uses the slot name as a hint (e.g., a slot named `repo` prefers a resource with alias `repo`). If ambiguous, the plan execution raises an error requiring explicit binding. * If no resource matches, the tool cannot be activated for this plan (a validation error is raised during plan creation). **2. Static Binding** The slot is hardcoded to a specific registered resource by name. This is useful for tools that always operate on the same resource, regardless of project context.

resources:
  docs:
    type: fs-mount
    access: read_only
    bind: local/company-docs # Static: always this resource
    description: "Company documentation corpus"
Static bindings are resolved at registration time and validated — the named resource must exist and be of the correct type. **3. Parameter Binding** The resource reference is passed as a tool argument at invocation time. This is useful for tools that operate on user-specified resources.

resources:
  target:
    type: git-checkout
    access: read_only
    from_param: repository # Bound from the "repository" input parameter
    description: "Repository to analyze"

input_schema:
  type: object
  properties:
    repository:
      type: string
      description: "Name of the registered resource to analyze"
  required: [repository]
The `from_param` field links a resource slot to an input parameter. At invocation time, the system resolves the parameter value as a resource name from the Resource Registry and validates type compatibility. ##### Binding Resolution Flow ```kroki-mermaid stateDiagram-v2 [*] --> ToolActivation: Actor references skill or tool node state "For Each Resource Slot" as ForEach { state binding_check <> [*] --> binding_check: Check binding type binding_check --> StaticBinding: has bind field binding_check --> ContextualBinding: no bind, no from_param binding_check --> ParameterBinding: has from_param state "Static Binding" as StaticBinding { [*] --> ResolveByName: Resolve from Resource Registry ResolveByName --> ValidateType: Validate type compatibility } state "Contextual Binding" as ContextualBinding { [*] --> SearchProject: Search plan's project resources SearchProject --> FilterByType: Filter by resource type state match_check <> FilterByType --> match_check match_check --> AutoBind: One match match_check --> TryAlias: Multiple matches match_check --> ValidationError: No matches TryAlias --> AliasMatch: Try alias/name match AliasMatch --> AutoBind: Match found AliasMatch --> ValidationError: No match } state "Parameter Binding" as ParameterBinding { [*] --> DeferBinding: Defer to invocation time } StaticBinding --> StoreBindings AutoBind --> StoreBindings ParameterBinding --> StoreBindings state "Store in ToolActivationContext" as StoreBindings } state "Tool Invocation" as Invocation { [*] --> ResolveParams: Resolve parameter-bound slots\nfrom invocation params [*] --> ValidateAccess: Ensure sandbox exists\nValidate access mode ResolveParams --> Execute ValidateAccess --> Execute state "Execute tool with bound resources" as Execute } ForEach --> Invocation Invocation --> [*] ``` ##### Built-in Tool Resource Bindings Built-in tools have **implicit resource slots** that do not need to be declared in YAML (they are hardcoded in the implementation): | Built-in Tool Group | Implicit Slot | Slot Type | Access | |---------------------|--------------|-----------|--------| | `file_operations` (read_file, write_file, edit_file, etc.) | `directory` | `fs-directory` or `git-checkout` | `read_write` | | `directory_operations` (create_directory, list_directory, etc.) | `directory` | `fs-directory` or `git-checkout` | `read_write` | | `search_operations` (search_files, find_definition, etc.) | `directory` | `fs-directory` or `git-checkout` | `read_only` | | `git_operations` (git_status, git_diff, git_log, etc.) | `repo` | `git-checkout` | `read_only` | Built-in tools accept both `fs-directory` and `git-checkout` types for file operations because a `git-checkout` resource's worktree root is an `fs-directory`. When binding to a `git-checkout`, the resource router automatically resolves to the worktree root `fs-directory` child for file operations. A standalone `fs-mount` resource also works — the router resolves through the `fs-mount` → root `fs-directory` chain. ##### Resource Discovery via Bindings The binding system enables powerful resource discovery queries: * **"What tools can modify this resource?"** → Find all tools with resource slots matching the resource's type and `access: read_write` or `access: write_only`. * **"What tools can read this virtual file?"** → Find the virtual file's physical children, then find tools with slots matching each physical resource's type. * **"What resources does this tool need?"** → Inspect the tool's declared resource slots. * **"Is this tool compatible with this project?"** → Check if the project's linked resources can satisfy all of the tool's required resource slots. #### Tool Registry CleverAgents maintains a **Tool Registry** — a persistent catalog of all independently registered tools: ```kroki-plantuml @startuml skinparam classAttributeIconSize 0 skinparam classFontSize 13 skinparam noteFontSize 11 skinparam defaultFontSize 12 class ToolRegistry { - toolIndex : Map -- + add(config_path) : ToolRecord + update(name, config_path) : ToolRecord + remove(name) : void + lookup(name) : ToolRecord + list(filters) : ToolRecord[] } class ToolRecord { + name : String + description : String + source : String {mcp|agent_skill|builtin|custom} + config_path : String + input_schema : JSONSchema + output_schema : JSONSchema + capability_metadata : CapabilityMetadata + resource_slots : List + code : String } ToolRegistry "1" *-- "0..*" ToolRecord : indexes > note right of ToolRegistry **Populated by:** - agents tool add CLI command - Dynamic refresh on MCP notifications **Consumed by:** - Skill registration - Actor graph construction - Resource binding resolution - Plan validation - Permission enforcement end note @enduml ``` The Tool Registry works alongside the Skill Registry (described in the Skills section). Skills reference tools by name from the Tool Registry; the Skill Registry's flattened tool sets are composed from Tool Registry entries plus any anonymous inline tools. #### Tool Interface and Architecture Each individual tool — whether independently registered or defined as an anonymous inline tool — conforms to a uniform interface regardless of its source: ```kroki-plantuml @startuml skinparam classAttributeIconSize 0 skinparam classFontSize 13 skinparam defaultFontSize 12 skinparam linetype ortho class Tool { } class Identity { + name : String + qualified_name : String + source : ToolSource } class Schema { + input_schema : JSONSchema + output_schema : JSONSchema } class CapabilityMetadata { + read_only : Boolean + writes : Boolean + write_scope : String + idempotent : Boolean + checkpointable : Boolean + side_effects : List + required_permissions : List + cost_profile : String + human_approval_required : Boolean } class ResourceBindings { + slots : Map } class ResourceSlot { + type : String + access : String + required : Boolean + bind : String + from_param : String + description : String } interface Lifecycle <> { + discover() : ToolDescriptor + activate() : void + execute(params, ctx) : Result + deactivate() : void } class ExecutionContext { + sandbox : Sandbox + plan : Plan + changes : List + resources : Map } enum ToolSource { mcp agent_skill builtin custom } Tool *-- Identity Tool *-- Schema Tool *-- CapabilityMetadata Tool *-- ResourceBindings Tool *-- Lifecycle Tool o-- ExecutionContext : uses at runtime > ResourceBindings *-- "0..*" ResourceSlot Identity --> ToolSource @enduml ``` Every tool implements the same four lifecycle methods. The **tool adapter layer** is responsible for translating source-specific behavior into these methods. #### Tool Adapter Layer Each tool source has a corresponding **adapter** that translates source-specific protocols into the uniform tool interface: ```kroki-plantuml @startuml skinparam classAttributeIconSize 0 skinparam classFontSize 13 skinparam defaultFontSize 12 interface "ToolInterface" as UTI <> { + discover() : ToolDescriptor + activate() : void + execute(params, ctx) : Result + deactivate() : void } class MCPToolAdapter { + discover() : ToolDescriptor .. tools/list RPC → descriptors .. + activate() : void .. spawn server, init JSON-RPC .. + execute(params, ctx) : Result .. tools/call RPC → result .. + deactivate() : void .. shutdown server .. } class AgentSkillAdapter { + discover() : ToolDescriptor .. parse SKILL.md frontmatter .. + activate() : void .. load SKILL.md body into agent context .. + execute(params, ctx) : Result .. agent follows instructions, runs scripts .. + deactivate() : void .. remove from context .. } class BuiltinAdapter { + discover() : ToolDescriptor .. return hardcoded descriptors .. + activate() : void .. no-op .. + execute(params, ctx) : Result .. call native Python impl .. + deactivate() : void .. no-op .. } MCPToolAdapter .up.|> UTI AgentSkillAdapter .up.|> UTI BuiltinAdapter .up.|> UTI @enduml ``` ##### MCPToolAdapter Bridges external MCP servers into the tool model: 1. **discover()**: Spawns the MCP server process (or connects to a remote Streamable HTTP endpoint), performs the MCP `initialize` handshake, negotiates capabilities, then calls `tools/list` to enumerate available tools. Each MCP tool becomes a separate `ToolDescriptor` with its `inputSchema` and inferred capability metadata. 2. **activate()**: Ensures the MCP server process is running and the JSON-RPC connection is healthy. For remote servers, validates the authentication token. Registers for `notifications/tools/list_changed` so CleverAgents can dynamically update available tools. 3. **execute()**: Translates a `Tool.execute(params, ctx)` call into an MCP `tools/call` JSON-RPC request. Before dispatching: - Rewrites file paths to sandbox-relative paths - Validates params against `inputSchema` - Checks capability metadata against plan permissions - Creates a checkpoint if the tool is marked checkpointable After the MCP tool returns its `content[]` response, the adapter: - Parses the result into the CleverAgents `Result` format - Records any resource mutations as `Change` objects in the plan's `ChangeSet` 4. **deactivate()**: Sends a clean shutdown to the MCP server process and closes the JSON-RPC connection. **Capability inference**: MCP tools expose limited metadata (name, description, inputSchema). The adapter infers extended capability metadata using heuristics: - Tools whose names contain `read`, `get`, `list`, `search`, `find` → `read_only: true` - Tools whose names contain `write`, `create`, `update`, `delete`, `set` → `writes: true` - All inferences can be overridden via the `overrides` block in tool or skill YAML ##### AgentSkillAdapter Bridges Agent Skills Standard (`SKILL.md` folders) into the tool model. Agent Skills are fundamentally different from MCP tools — they are **instruction-driven** rather than **schema-driven**. An Agent Skill is not a single function call; it is a bundle of procedural knowledge that an LLM agent loads into its context and follows. 1. **discover()**: Scans the configured skill directory for a `SKILL.md` file. Parses only the YAML frontmatter (`name`, `description`, optional `compatibility`, `metadata`, `allowed-tools`) to produce a lightweight `ToolDescriptor`. This metadata is injected into the agent's system prompt in a structured format so the LLM can decide when the skill is relevant:

   <style="color: cyan; font-weight: 600;">available_agent_skills>
     <style="color: cyan; font-weight: 600;">agent_skill>
       <style="color: cyan; font-weight: 600;">name>deploy-to-staging</style="color: cyan; font-weight: 600;">name>
       <style="color: cyan; font-weight: 600;">description>Deploy the current branch to the staging environment.</style="color: cyan; font-weight: 600;">description>
       <style="color: cyan; font-weight: 600;">tool>local/deploy-staging</style="color: cyan; font-weight: 600;">tool>
     </style="color: cyan; font-weight: 600;">agent_skill>
   </style="color: cyan; font-weight: 600;">available_agent_skills>
   
Discovery is **low-cost** — only ~50–100 tokens per Agent Skill for metadata. The full instructions are not loaded until activation. 2. **activate()**: When the LLM agent determines (or is instructed) that a task matches the skill's description, the adapter loads the full `SKILL.md` Markdown body into the agent's active context. This injects step-by-step instructions, examples, edge cases, and references to bundled scripts. The tool is now "active" — the agent has the procedural knowledge to execute it. If the skill references additional files (`references/*.md`, `scripts/*.py`, `assets/*`), these are made available on demand — the agent can read them as needed, following the Agent Skills Standard's **progressive disclosure** model. 3. **execute()**: Unlike MCP tools (which are single RPC calls), Agent Skill execution is **agent-mediated**. The LLM agent follows the loaded instructions, potentially: - Running bundled scripts via shell execution (sandboxed) - Reading reference files for additional context - Using other available tools (e.g., built-in file operations) as part of the procedure - Making multiple tool calls in sequence to accomplish the workflow The adapter wraps this execution in a tool execution context so that all mutations are tracked, sandboxed, and checkpointable. Script execution respects the `allowed_tools` and `sandbox_policy` declared in the tool's YAML. 4. **deactivate()**: Removes the skill's instructions from the agent's active context to free up token budget. The skill's metadata remains available for re-activation. **Key design principle**: Agent Skills extend the agent's *knowledge*, not just its *toolset*. An Agent Skill can teach an agent a multi-step workflow that involves calling multiple other tools, making decisions based on intermediate results, and following domain-specific best practices — something a single MCP tool call cannot express. ##### BuiltinToolAdapter Wraps CleverAgents' native resource operations as tools: 1. **discover()**: Returns hardcoded `ToolDescriptor` objects for each built-in operation. These descriptors have fully specified capability metadata since the implementation is first-party. 2. **activate()**: No-op. Built-in tools are always available. 3. **execute()**: Calls the native Python implementation directly. Built-in tools operate through the resource abstraction layer, automatically integrating with sandbox path mapping, change tracking, and checkpointing. 4. **deactivate()**: No-op. **Built-in tool groups:** **File Operations (`file_operations`):**

read_file(path: str) -> str
write_file(path: str, content: str) -> None
edit_file(path: str, edits: list[Edit]) -> None
delete_file(path: str) -> None
move_file(source: str, destination: str) -> None
copy_file(source: str, destination: str) -> None
**Directory Operations (`directory_operations`):**

create_directory(path: str) -> None
list_directory(path: str, pattern: str = "*") -> list[str]
delete_directory(path: str, recursive: bool = False) -> None
**Search Operations (`search_operations`):**

search_files(pattern: str, content_pattern: str = None) -> list[Match]
find_definition(symbol: str) -> list[Location]
find_references(symbol: str) -> list[Location]
**Git Operations (`git_operations`, when resource is a git repository):**

git_status() -> GitStatus
git_diff(path: str = None) -> str
git_log(count: int = 10) -> list[Commit]
git_blame(path: str) -> list[BlameLine]
Each built-in tool: * Has fully defined capability metadata * Operates through the resource abstraction layer * Automatically tracks changes to the ChangeSet * Respects sandbox boundaries and deny-lists #### Tool Capability Metadata (Critical for Safety) MCP's metadata is not sufficient (read-only/idempotent is not enough; write scope is unclear). CleverAgents extends every tool — regardless of source — with a uniform capability metadata schema:

capability:
  read_only: bool # Whether tool only performs read operations
  writes: bool # Whether tool can modify resources
  write_scope:             # What the tool is allowed to mutate
    - file_paths: ["src/**", "tests/**"]     # Path patterns within bound resources
    - resource_slots: ["repo", "db"]         # Resource slot names (from resource bindings)
    - environment: container | host
  idempotent: bool # Whether repeated calls produce same result
  checkpointable: bool # Whether tool supports checkpoint/rollback
  checkpoint_scope: str # What can be rolled back (file, transaction, commit, snapshot)
  side_effects:            # Non-reversible effects
    - install_packages
    - mutate_infra
    - send_email
  required_permissions:    # Permissions needed to invoke
    - resource:write
    - sandbox:shell
  cost_profile:            # Usage constraints
    rate_limit: "10/min"
    estimated_cost: "$0.01/call"
  human_approval_required: bool # Whether a human must approve invocation
**Where metadata comes from per source:** | Source | Metadata origin | Override mechanism | |--------|-----------------|--------------------| | Built-in | Hardcoded in implementation | Not overridable (authoritative) | | MCP | Inferred from tool name/description + MCP annotations | `overrides` block in tool or skill YAML; `override` at skill/actor reference point | | Agent Skill | Declared in SKILL.md frontmatter `metadata` + inferred from `allowed-tools` | Tool YAML `capability` block; `override` at skill/actor reference point | | Custom | Manually declared in tool YAML `capability` block | `override` at skill/actor reference point (author is the source of truth for registered values) | #### Read-Only Actions and Tool Permissions When an action is marked `read_only: true`, it can **only use tools that have `read_only: true`** in their capability metadata. This is enforced at runtime by the tool execution context — any attempt to invoke a tool with `writes: true` from a read-only plan raises a `PermissionDeniedError`. #### Tool Execution Flow When an LLM agent decides to use a tool (regardless of source), the following flow occurs through the unified execution pipeline:

1. LLM generates tool call
   e.g., edit_file(path="src/main.py", changes=[...])
   or:   local/github.create_issue(title="Bug fix", body="...")
   or:   (activates Agent Skill "deploy-to-staging" via instructions)
                                ↓
2. Tool Router receives call
   - Resolves tool by name from the Tool Registry or actor's skill tool sets
   - Validates parameters against inputSchema
   - Checks capability metadata against plan's permission policy:
     • Is this tool in allowed skill categories?
     • Does the plan allow writes?
     • Is human approval required?
   - If denied → return PermissionDeniedError to LLM
                                ↓
3. Resource Binding Resolution & Sandbox Context
   - Resolve resource bindings for this tool:
     • Static bindings: already resolved at registration
     • Contextual bindings: resolve from plan's project resources
     • Parameter bindings: resolve from invocation arguments
   - Validate resource type compatibility for each slot
   - Validate access mode (e.g., read_write tool on read_only resource → error)
   - Ensure sandbox exists for each bound resource (lazy sandboxing)
   - Maps logical paths to sandbox-relative paths via bound resource handlers
   - Inject bound resources into ctx.resources[slot_name]
   - If tool is checkpointable → create pre-execution checkpoint
                                ↓
4. Adapter-Specific Execution
   - MCP: sends tools/call JSON-RPC to server process
   - Agent Skill: agent follows loaded SKILL.md instructions,
     running scripts and tools in sandboxed shell
   - Built-in: calls native Python implementation directly
   - Custom: executes inline code with sandboxed context
                                ↓
5. Change Recording
   - If tool modified resources → create Change record(s)
   - Append Change(s) to plan's ChangeSet
   - Update sandbox state
   - If checkpointable → record checkpoint for rollback
                                ↓
6. Result Return
   - Normalize result to uniform Result type
   - Return to LLM agent for continued reasoning
#### Change Tracking from Tool Invocations **Critical Architecture Point:** The ChangeSet is NOT built by parsing LLM output. It is built by recording the effects of tool invocations:

class ToolExecutionContext:
    """Context provided to every tool execution, regardless of source."""
    
    def __init__(self, plan: Plan, sandbox: Sandbox,
                 resources: dict[str, BoundResource]):
        self.plan = plan
        self.sandbox = sandbox
        self.resources = resources    # slot_name → BoundResource
        self.changes: list[Change] = []
    
    def record_change(self, change: Change) -> None:
        """Record a change made by a tool."""
        self.changes.append(change)
        self.plan.changeset.add_change(change)


class WriteFileTool:
    """Example: built-in tool for writing files."""
    
    def execute(self, path: str, content: str, ctx: ToolExecutionContext) -> None:
        handler = ctx.sandbox.get_handler(path)
        change = handler.write(path, content, ctx.sandbox)
        ctx.record_change(change)
This approach means: * Every resource modification is explicit and tracked * The ChangeSet accurately reflects what was done, not what was said * Rollback is precise (replay inverse of recorded changes) * Audit logs show exactly which tool invocation produced each change #### MCP Integration Details ##### MCP Concepts Mapping | MCP Concept | CleverAgents Equivalent | Extension | |------------|------------------------|-----------| | Tool | Independently registered Tool (source: mcp), referenced by skills | Extended capability metadata (write_scope, checkpointable, side_effects) | | Resource | Resource | Extended to support both read AND write operations | | Prompt | Action template | Full plan lifecycle (Strategize → Execute → Apply) | | Server | MCP Server connection (declared in tool or skill YAML) | Managed by MCPToolAdapter with lifecycle and reconnection | | JSON-RPC | Internal adapter protocol | Abstracted behind Tool.execute() | ##### MCP Server Lifecycle Management CleverAgents manages MCP server processes as part of the tool/skill/actor lifecycle: ```kroki-mermaid sequenceDiagram participant CLI as CLI participant Reg as Registry participant ActorRT as Actor Runtime participant Adapter as MCPToolAdapter participant MCP as MCP Server note over CLI,Reg: Phase 1 - Registration CLI->>Reg: agents tool add or skill add Reg->>Reg: Validate server command or endpoint Reg->>Reg: Store server config in record note over ActorRT,MCP: Phase 2 - Actor Activation ActorRT->>Adapter: Activate (for each MCP server) Adapter->>MCP: Spawn process (stdio) or connect (HTTP) Adapter->>MCP: MCP initialize handshake MCP-->>Adapter: Capabilities Adapter->>MCP: tools list MCP-->>Adapter: Tool descriptors Adapter->>MCP: Subscribe notifications tools list_changed note over ActorRT,MCP: Phase 3 - Execution ActorRT->>Adapter: LLM generates tool call Adapter->>MCP: tools call (JSON-RPC) MCP-->>Adapter: Result Adapter->>ActorRT: Result + Change tracking note over ActorRT,MCP: Phase 4 - Deactivation ActorRT->>Adapter: Deactivate Adapter->>MCP: Clean shutdown ``` ##### Sandbox Path Rewriting for MCP Tools MCP servers operate on real filesystem paths, but CleverAgents executes plans in sandboxes. The MCPToolAdapter transparently rewrites paths:

# Before sending to MCP server:
# Logical path: "src/main.py"
# Sandbox path: "/tmp/sandbox-01HXM/worktree/src/main.py"
# The adapter rewrites the tool arguments so the MCP server
# operates on sandboxed state without knowing about the sandbox.
This ensures MCP tools respect sandbox boundaries even though they have no awareness of the CleverAgents sandbox model. #### Agent Skills Integration Details ##### Discovery and Progressive Disclosure Agent Skills follow a three-tier progressive disclosure model that maps directly to the tool lifecycle: | Tier | What loads | When | Token cost | |------|-----------|------|------------| | **Metadata** | `name` + `description` from SKILL.md frontmatter | Tool registration / actor activation | ~50–100 tokens per Agent Skill | | **Instructions** | Full SKILL.md Markdown body | When LLM determines task matches the skill (activate phase) | Recommended < 5000 tokens | | **Resources** | `scripts/`, `references/`, `assets/` | On demand during execution | Variable | This means an actor can have dozens of Agent Skill tools available but only pay the token cost for their metadata at startup. Full instructions load only when relevant. ##### Agent Skills vs. MCP Tools: When to Use Which | Dimension | MCP Tools | Agent Skills | |-----------|-----------|--------------| | **Interaction model** | Single function call with JSON params/result | Multi-step procedure with instructions the agent follows | | **Knowledge type** | "Here is a function you can call" | "Here is how to accomplish a complex task" | | **Statefulness** | Stateless per-call | Stateful across multiple tool calls within a procedure | | **Authoring** | Implement an MCP server (code) | Write a SKILL.md file (prose + optional scripts) | | **Portability** | Any MCP-compatible host | Any Agent Skills-compatible agent | | **Best for** | Atomic operations (CRUD, queries, API calls) | Complex workflows (code review processes, deployment procedures, data analysis pipelines) | Both can coexist in the same skill. A common pattern is an Agent Skill tool that teaches the agent a workflow which involves calling multiple MCP tools:

Agent Skill Tool: "local/deploy-staging"
  SKILL.md instructions:
    1. Run tests using the built-in shell tool
    2. Create a PR using the GitHub MCP tool (create_pull_request)
    3. Wait for CI using the GitHub MCP tool (get_check_runs)
    4. Deploy using the AWS MCP tool (ecs_update_service)
    5. Verify deployment using the HTTP MCP tool (fetch_url)
#### Tool-Level Checkpointability Each tool declares whether it supports checkpoint/rollback via its capability metadata. Checkpointing behavior varies by source: | Source | Checkpoint mechanism | |--------|---------------------| | **Built-in file ops** | Snapshot file state pre-modification; rollback restores file content | | **Built-in git ops** | Create commit or stash; rollback via `git reset` / `git checkout` | | **MCP tools** | Adapter-created checkpoints of affected resources; rollback replays inverse operations where possible | | **Agent Skills** | Composite — each sub-tool-call within the skill's execution is individually checkpointed | | **Custom (containerized)** | Filesystem snapshot or container image layer; rollback restores snapshot | Checkpointing is easier when tool scope is constrained (e.g., "only files within a sandbox worktree + git"). The capability metadata's `checkpoint_scope` field communicates what granularity of rollback is supported. When `require_checkpoints` is enabled on a plan, the plan may **only** use tools that have `checkpointable: true`. If disabled, the plan may use more generic/unsafe tools with fewer restrictions (useful for low-stakes tasks). ### Skills #### What a Skill Is A **skill** is a namespaced, reusable **collection of tools** that is registered in the system via its own YAML configuration file and managed through `agents skill` CLI commands. Skills are the unit of capability composition in CleverAgents — they define *what an actor can do* by assembling tools into coherent, reusable bundles. A skill is **not** a single tool. It is a **container** that references one or more tools (by name from the Tool Registry) and/or defines anonymous inline tools, along with metadata describing the collection's purpose, capabilities, and safety characteristics. Tools are independently registered, callable operations (see the **Tools** section above). Skills organize them into reusable groups. **Key properties of a skill:** * **Named and namespaced**: Skills follow the same `/` naming convention as actors, tools, actions, and plans (e.g., `local/github-ops`, `cleverthis/file-management`, `local/deploy-tools`). * **Defined in their own YAML files**: Skills are NOT defined inline in actor configurations. They have their own configuration files and are managed as independent, reusable entities. * **A collection of tools**: Each skill references named tools from the Tool Registry and/or defines anonymous inline tools. Skills can also expose tools from MCP servers, Agent Skills Standard folders, and built-in tool groups. * **Hierarchically composable**: A skill can include other skills by reference, inheriting all of their tools. This enables layered composition — a "full-stack" skill might include a "file-ops" skill, a "git-ops" skill, and a "github" skill. When including a sub-skill, individual tool metadata can optionally be overridden. * **Referenced by actors**: Actors reference skills by fully-qualified name. The actor's graph gains access to all tools within the referenced skills. #### The Skill / Tool Distinction ```kroki-plantuml @startuml skinparam packageStyle rectangle skinparam defaultFontSize 12 skinparam componentFontSize 12 package "Skill: local/devops-toolkit" as Skill { package "builtin: file_operations" as FileOps { component [read_file()] as T1 component [write_file()] as T2 component [edit_file()] as T3 } package "builtin: git_operations" as GitOps { component [git_status()] as T4 component [git_diff()] as T5 } package "mcp: github-server" as GH { component [create_issue()] as T6 component [create_pr()] as T7 component [list_repos()] as T8 } package "custom" as Custom { component [run_migrations()] as T9 } package "Included Skills" as Includes { component [local/pdf-processing\n(adds pdf tools)] as I1 component [local/data-analysis\n(adds analysis tools)] as I2 } } @enduml ``` **Tools** are independently registered, atomic units of execution (see the **Tools** section above for full details). Each tool has: * A namespaced name, description, and JSON Schema for inputs/outputs * Its own YAML configuration file, registered via `agents tool add` * A source type (mcp, agent_skill, builtin, custom) * Capability metadata (read_only, writes, checkpointable, etc.) * A lifecycle: `discover()`, `activate()`, `execute(params, ctx)`, `deactivate()` **Skills** are the organizational units. Each skill has: * A namespaced name * A YAML configuration file defining its tool composition * Zero or more **named tool references** (pointing to independently registered tools in the Tool Registry) * Zero or more **anonymous inline tools** (one-off tools defined directly in the skill YAML) * Zero or more included child skills (whose tools are merged in, with optional per-tool metadata overrides) * Tool sources: MCP servers, Agent Skills folders, built-in tool groups * Metadata (description, capability summary) When an actor references a skill, it gains access to the **flattened set of all tools** — named tool references, anonymous tools, tools from MCP/Agent Skills/builtins, and those inherited from included child skills. #### Skill Configuration (YAML) Skills are defined in their own YAML configuration files, separate from tool and actor configurations. A skill YAML file declares which registered tools it includes (by name), which other skills it includes, and optionally defines anonymous inline tools:

# File: skills/devops-toolkit.yaml
cleveragents:
  version: "3.0"

skill:
  name: local/devops-toolkit
  description: "Full-stack development tools for file ops, git, GitHub, and deployment"

  # ── Named Tool References ───────────────────────────────
  # Reference independently registered tools by name.
  # These tools must already be registered via `agents tool add`.
  # Optional metadata overrides can be applied per tool.
  tools:
    - local/run-migrations                  # Simple reference, use as registered
    - name: local/deploy-staging            # Reference with metadata override
      override:
        capability:
          human_approval_required: true # Require approval in this skill context

  # ── Include other skills ─────────────────────────────────
  # All tools from included skills become part of this skill.
  # Included skills must already be registered in the system.
  # Individual tools from included skills can have metadata overridden.
  includes:
    - local/file-ops           # built-in file + directory + search tools
    - local/git-ops            # built-in git tools
    - name: local/github       # MCP-based GitHub tools, with per-tool overrides
      tool_overrides:
        - tool: local/create-github-issue
          override:
            capability:
              write_scope: [github:issues:org-only]

  # ── MCP Server Tools ─────────────────────────────────────
  # Connect to MCP servers and expose their tools.
  # Tools discovered from MCP servers are auto-registered in the
  # Tool Registry if not already present.
  mcp_servers:
    - name: linear
      command: "npx @anthropic/mcp-linear"
      env:
        LINEAR_API_KEY: "${LINEAR_API_KEY}"
      # Optional: override inferred capability metadata per tool
      overrides:
        - tool: create_issue
          writes: true
          write_scope: [linear:issues]
        - tool: list_issues
          read_only: true

  # ── Agent Skills (SKILL.md folders) ──────────────────────
  # Each Agent Skill folder is loaded as a composite tool.
  # The agent discovers it via metadata, activates it by
  # loading SKILL.md instructions, and follows them.
  agent_skills:
    - path: ./skills/code-review-checklist
      sandbox_policy: none

  # ── Built-in Tool Groups ─────────────────────────────────
  # Opt-in to built-in tool groups provided by CleverAgents.
  builtins:
    - group: shell_operations

  # ── Anonymous Tools ──────────────────────────────────────
  # Inline tool definitions for one-off, skill-specific operations.
  # Same format as a named tool YAML body but without a name.
  # These are NOT registered in the Tool Registry and are NOT reusable.
  anonymous_tools:
    - description: "One-off cleanup for legacy migration artifacts"
      input_schema:
        type: object
        properties:
          directory: { type: string }
      capability:
        writes: true
        checkpointable: true
        checkpoint_scope: file
      code: |
        import os, glob
        directory = params["directory"]
        removed = []
        for f in glob.glob(os.path.join(ctx.sandbox.root, directory, "*.legacy")):
            os.remove(f)
            removed.append(f)
        return {"removed": removed, "count": len(removed)}
Here is an example of a simpler skill that wraps only built-in tools, suitable for common reuse:

# File: skills/file-ops.yaml
cleveragents:
  version: "3.0"

skill:
  name: local/file-ops
  description: "File and directory operations"

  builtins:
    - group: file_operations      # read, write, edit, delete, move, copy
    - group: directory_operations  # create, list, delete dirs
And an example of a skill that is purely MCP-based:

# File: skills/github.yaml
cleveragents:
  version: "3.0"

skill:
  name: local/github
  description: "GitHub operations via MCP"

  mcp_servers:
    - name: github
      command: "npx @anthropic/mcp-github"
      env:
        GITHUB_TOKEN: "${GITHUB_TOKEN}"
      overrides:
        - tool: create_issue
          writes: true
          write_scope: [github:issues]
          checkpointable: false
        - tool: create_pull_request
          writes: true
          write_scope: [github:pulls]
          checkpointable: false
        - tool: list_repos
          read_only: true
        - tool: get_file_contents
          read_only: true
#### Skill Hierarchy and Composition Skills can include other skills via the `includes` field. When a skill includes another, all tools from the child skill (and transitively, all tools from any skills *it* includes) become part of the parent skill's flattened tool set.

local/full-stack-dev
  ├── includes: local/file-ops
  │     └── builtins: file_operations, directory_operations
  ├── includes: local/git-ops
  │     └── builtins: git_operations
  ├── includes: local/github
  │     └── mcp_servers: github (create_issue, create_pr, list_repos, ...)
  ├── agent_skills: code-review-checklist
  └── tools: local/run-migrations, local/deploy-staging  (named tool refs)

  Flattened tool set available to actors referencing local/full-stack-dev:
    read_file, write_file, edit_file, delete_file, move_file, copy_file,
    create_directory, list_directory, delete_directory,
    git_status, git_diff, git_log, git_blame,
    create_issue, create_pr, list_repos, get_file_contents,
    code-review-checklist (agent skill),
    local/run-migrations, local/deploy-staging (named tools)
**Rules for skill composition:** 1. **Circular includes are forbidden.** The system validates the include graph at registration time and rejects cycles. 2. **Tool name conflicts**: If two included skills provide tools with the same name, the conflict is resolved by qualification — the tool must be referenced as `.` (e.g., `local/github.create_issue` vs `local/linear.create_issue`). Direct tools (defined in the skill itself) take precedence over included tools. 3. **Included skills must be registered** before the including skill can be added. The `agents skill add` command validates this. 4. **Depth is unlimited** but the flattened tool set is computed at registration time and cached. Deep hierarchies do not incur runtime overhead. #### Skill Registration and Management Skills are managed through the `agents skill` CLI commands. Named tools referenced by skills must first be registered via `agents tool add` (see the **Tools** section):

# First, register any named tools the skill will reference
agents tool add --config ./tools/run-migrations.yaml local/run-migrations
agents tool add --config ./tools/deploy-staging.yaml local/deploy-staging

# Then register the skill (which references those tools by name)
agents skill add --config ./skills/devops-toolkit.yaml local/devops-toolkit

# Update an existing skill (re-reads the config file, overwrites registration)
agents skill add --config ./skills/devops-toolkit.yaml --update local/devops-toolkit

# List all registered skills
agents skill list

# Show details for a skill (tools, includes, metadata)
agents skill show local/devops-toolkit

# List all tools provided by a skill (flattened, including from child skills)
agents skill tools local/devops-toolkit

# Remove a skill
agents skill remove local/devops-toolkit
Once registered, a skill is available to be referenced by any actor configuration. Skills persist in the database (local or server) and follow the same namespace rules as actors, tools, and actions. #### Actor References to Skills and Tools Actors reference skills **by name** to make collections of tools available for LLM tool-calling. Additionally, actor graphs can include **tool nodes** that directly reference named tools or define anonymous inline tools (see **Nodes in the Graph** in the Actor section). The actor's configuration lists which skills it should have access to:

# File: actors/code-assistant.yaml
cleveragents:
  version: "3.0"
  default_actor: code_assistant

actors:
  code_assistant:
    type: llm
    config:
      actor: anthropic/claude-3-opus
      temperature: 0.3
      system_prompt: |
        You are a code assistant with access to file, git, and GitHub tools.
        Current task: {{ context.task_description }}

    # Reference skills by fully-qualified name.
    # All tools from these skills become available to this actor.
    skills:
      - local/file-ops
      - local/git-ops
      - local/github

  # Or reference a single composite skill that includes all of the above
  full_stack_assistant:
    type: llm
    config:
      actor: anthropic/claude-3-opus
      system_prompt: |
        You are a full-stack development assistant.

    skills:
      - local/full-stack-dev    # includes file-ops, git-ops, github, etc.
**Server-qualified skill references**: When connected to multiple servers, skills can be disambiguated with a server prefix, same as actors:

skills:
  - dev:freemo/custom-analysis     # from dev server, personal namespace
  - prod:cleverthis/deploy-tools   # from prod server, org namespace
  - local/file-ops                 # local skill
#### Skill Registry To enable plan validation, permission enforcement, and discovery at scale, CleverAgents maintains two registries that work together: 1. **Tool Registry** — a persistent catalog of all independently registered tools (described in the **Tools** section above). Managed via `agents tool add/remove/list/show`. 2. **Skill Registry** — a persistent catalog of all registered skills and their flattened tool sets. The Skill Registry composes its tool sets by resolving named tool references from the Tool Registry, incorporating anonymous inline tools, and merging tools from included child skills. ```kroki-plantuml @startuml skinparam classAttributeIconSize 0 skinparam classFontSize 13 skinparam defaultFontSize 12 class SkillRegistry { - skillIndex : Map -- + add(config_path) : SkillRecord + update(name, config_path) : SkillRecord + remove(name) : void + lookup(name) : SkillRecord + list(filters) : SkillRecord[] + tools(name) : ToolDescriptor[] + validate_plan(plan) : ValidationResult + refresh(name) : void } class SkillRecord { + name : String + description : String + config_path : String + includes : List + tool_refs : List + anonymous_tools : List + flattened_tools : List + overrides : Map + capability_summary : CapabilitySummary } SkillRegistry "1" *-- "0..*" SkillRecord : indexes > note right of SkillRegistry **Populated by:** - agents skill add CLI command - Dynamic refresh on MCP notifications **Depends on:** - Tool Registry (resolve named tool references) **Consumed by:** - Actor activation - Plan validation - Permission enforcement - Agent context injection end note @enduml ``` Both registries persist in the database (local SQLite or server). MCP server tools are refreshed dynamically when `notifications/tools/list_changed` events are received. ### Session #### What a Session Is A **session** is a user's interactive thread with CleverAgents across time. A session should: * maintain conversational continuity, * store plan references, * persist memory (if enabled), * provide a UI anchor (CLI invocation, TUI workspace, web session). #### Session and Memory Persistence The notes include a known issue: conversation history can be lost between CLI invocations depending on connection string configuration, implying the system needs a stable memory service backend. Therefore, CleverAgents should specify: * sessions have stable IDs * sessions can be resumed * session storage backend is configured explicitly * if session persistence is disabled, the UX should be explicit about it (no silent history loss) ### Server #### What a Server Is A server is an optional mode that enables: * multi-user access * shared org namespaces (`/` and `/`) * persistent plan records * remote plan execution * permissioning and governance **Single-user local mode is the default** (so setup is easy), but the architecture anticipates server mode for shared skills and org-level actions. #### Client-Only vs Server Mode | Mode | Description | Plan Execution | |------|-------------|----------------| | **Client-only** | No server connection. All data in local database. | Always local | | **Server mode** | Connected to a CleverAgents server. Namespaced items sync. | Local or server | **It is possible to run a client with no server at all.** Server is optional. #### Plan Execution Location Where a plan executes depends on the project type: | Project Type | Client Execution | Server Execution | |--------------|-----------------|------------------| | **Local** (has local-only resources) | ✅ Yes | ❌ No (server can't access local resources) | | **Remote** (all resources remotely accessible) | ✅ Yes | ✅ Yes | When acting on **local projects**, the client must be running because only the client can access local resources. **Remote projects** can execute on either: - The client (if user prefers local execution) - The server (for long-running plans, since client may be transient) #### Server Execution Benefits Server execution is useful when: * Plans take a long time to execute * Client may disconnect (laptop closes, network issues) * Multiple team members need to monitor plan progress * Centralized logging and auditing required #### No Plan Queuing **Plans are not queued.** When a plan is used on projects and executed, it runs immediately. There is no worker queue or delayed execution model. #### Multi-user Risks and Prompt Injection Prompt injection isn't critical in single-user mode but becomes important for multi-user server environments. Server mode must include: * permission boundaries * prompt sanitization / safe templating * resource access controls * auditing ### Permissions Permissions exist at multiple layers: #### 1) Namespace-level permissions * Who can create/edit org entities (actions, actors, tools, skills, resources, projects)? * Who can run them? #### 2) Project-level permissions * Who can modify project resources? * Who can apply changes? #### 3) Plan-level permissions * Can this plan write? * Does it require approvals? * Can it access restricted skills? #### 4) Skill-level permissions * Some skills should require: * explicit user approval per call, or * elevated role membership. #### 5) Resource-level permissions * Who can register/modify/remove resources in a namespace? * Who can link a resource to a project? * Per-resource access control: * Some resources may be restricted to specific roles (e.g., production databases) * Read-only vs. read-write access can be enforced per resource per project (via project-level overrides) * Sensitive resources can require explicit approval before sandbox creation or tool binding #### Approval Gates by Phase (Recommended) A simple and powerful governance model: * Strategize: generally safe, read-only → minimal restrictions * Execute: writes occur but sandboxed → moderate restrictions * Apply: writes are real → strict restrictions + optional mandatory review This aligns with the four-phase model's safety rationale. ### Resources A **resource** is a namespaced, independently registered entity representing anything that a plan can reason about or manipulate — git repositories, filesystems, databases, APIs, documents, and more. Resources are **first-class citizens** in CleverAgents, managed through the `agents resource` CLI commands and stored in the **Resource Registry**. **CleverAgents extends the MCP resource concept** to support both read AND write operations (MCP resources are read-only). Unlike MCP's flat resource model, CleverAgents resources form a **directed acyclic graph (DAG)** with parent/child relationships, support **physical vs virtual** distinction for content identity tracking, and are governed by a **resource type system** that constrains their structure and behavior. Resources are registered independently of projects. Projects **link** to resources from the Resource Registry — a resource can be linked to multiple projects, enabling shared resources across teams and workflows. #### What a Resource Is A resource has: * **Name and namespace**: Resources follow the same `/` naming convention as actors, tools, skills, and other entities (e.g., `local/api-repo`, `cleverthis/staging-db`). * **Resource type**: Every resource has a type (e.g., `git`, `fs-mount`, `git-branch`, `fs-file`) that determines its properties, CLI arguments, allowed parent/child relationships, sandbox strategy, and handler implementation. * **Physical or virtual nature**: Resources are either **physical** (a specific, concrete manifestation) or **virtual** (an abstract identity linking equivalent physical resources). This is determined by the resource type. * **Value/properties**: Type-specific properties (a file path, a URL, a connection string, a commit hash, etc.). * **Parent/child relationships**: Resources form a DAG. A resource can have multiple parents and multiple children, subject to type constraints. * **Capabilities**: Whether the resource is readable, writable, sandboxable, and checkpointable. #### Resource Types A **resource type** is a schema-level definition that constrains a category of resources. Resource types define: * **CLI arguments**: What arguments `agents resource add ` accepts (for user-addable types), including which are required vs optional and validation rules. * **Physical or virtual**: Whether instances of this type are physical or virtual resources. * **Allowed parent types**: What resource types are valid parents. * **Allowed child types**: What resource types are valid children, and whether they are auto-discovered or manually linkable. * **Auto-discovery behavior**: What child resources are automatically created when an instance of this type is registered (e.g., registering a `git-checkout` resource auto-discovers a `git` child and an `fs-directory` child for the worktree root; the `git` child in turn auto-discovers remotes, branches, commits, and tree entries). * **User addable**: Whether users can create instances of this type directly via `agents resource add `. Types with `user_addable: false` are only generated as auto-discovered children of other resources. * **Sandbox strategy**: The default sandboxing approach for instances of this type. * **Handler**: The resource handler implementation that provides read/write/sandbox/checkpoint operations. ##### Built-in Resource Types Built-in types are organized into three layers: **git** (version control structure), **git-checkout** (a composition that bridges git metadata with a local directory), and **filesystem** (physical files on disk). Virtual types link equivalent physical resources across these layers through content/identity matching. There are 24 built-in types total (15 physical + 9 virtual), of which 4 are user-addable (`git-checkout`, `git`, `fs-mount`, `fs-directory`). Each table below includes the full parent/child relationship constraints. **Allowed Parents** lists what types may be a parent of this type, with cardinality (how many parents of that type are allowed) and whether the relationship is required or optional. **Allowed Children** lists what types may be children, with cardinality and whether they are required or optional. `0..*` means zero or more, `1` means exactly one (required), `0..1` means zero or one (optional). **Physical types — Git layer** (version control structure — every instance is a concrete manifestation in a specific repository): | Type | User Addable | Sandbox | Allowed Parents | Allowed Children | Description | |------|-------------|---------|-----------------|------------------|-------------| | `git` | yes | `none` | `git-checkout` (0..1, optional) | `git-remote` (0..*, optional), `git-branch` (0..*, optional), `git-tag` (0..*, optional), `git-commit` (0..*, optional), `git-stash` (0..*, optional), `git-submodule` (0..*, optional) | A git repository — the object database, refs, and full history. Accessible via a local `.git` directory path or a remote URL. Every `git` resource is a specific, concrete repo instance (local or hosted on a remote server). | | `git-remote` | no | `none` | `git` (1, required) | (none) | A remote URL configured on a git repo (e.g., `origin → https://github.com/org/repo`). Auto-discovered child of `git`. Optionally linked as a child of a `remote` virtual resource when the same URL appears in multiple repos. | | `git-branch` | no | (inherits) | `git` (1, required) | `git-commit` (0..*, optional) | A named branch ref (e.g., `main`, `feature/auth`). Auto-discovered child of `git`. Optionally linked as a child of a `branch` virtual resource when the same branch name + HEAD exists in multiple repos. | | `git-tag` | no | (inherits) | `git` (1, required) | (none) | A tag ref — lightweight or annotated (e.g., `v1.0.0`). Auto-discovered child of `git`. Optionally linked as a child of a `tag` virtual resource when the same tag name + target exists in multiple repos. | | `git-commit` | no | (inherits) | `git-branch` (1..*, required), `git` (1, required) | `git-tree` (1, required) | A specific commit object. Auto-discovered child of `git-branch` (a commit can belong to multiple branches). Each commit contains exactly one root `git-tree`. Also a direct child of `git` for ancestry traversal. Optionally linked as a child of a `commit` virtual resource when the same commit hash exists in multiple repos (e.g., shared history between fork and upstream). | | `git-tree` | no | (inherits) | `git-commit` (1..*, required) | `git-tree-entry` (0..*, optional), `git-tree` (0..*, optional) | A tree object — a directory listing at a specific commit. Each tree contains entries (blobs and subtrees). Auto-discovered child of `git-commit`. Trees are recursive: a tree can contain subtrees as children. Optionally linked as a child of a `tree` virtual resource when the same tree hash exists across commits/repos. | | `git-tree-entry` | no | (inherits) | `git-tree` (1, required) | (none) | A blob entry in a tree — a specific file's content at a specific path and mode. Auto-discovered child of `git-tree`. Represents the leaf of git's content-addressable storage. Optionally linked as a child of a `file` virtual resource when byte-identical content with the same name and permissions exists in the filesystem layer. | | `git-stash` | no | (inherits) | `git` (1, required) | (none) | A stash entry (e.g., `stash@{0}`). Auto-discovered child of `git`. Represents work-in-progress saved via `git stash`. | | `git-submodule` | no | (inherits) | `git` (1, required) | (none) | A submodule reference — a pointer to another git repository at a specific commit and path. Auto-discovered child of `git`. Optionally linked as a child of a `submodule` virtual resource when the same submodule URL + path exists in multiple repos. | **Physical types — Git checkout** (composition: git metadata + local worktree directory): | Type | User Addable | Sandbox | Allowed Parents | Allowed Children | Description | |------|-------------|---------|-----------------|------------------|-------------| | `git-checkout` | yes | `git_worktree` | (none — always a top-level resource) | `git` (1, required), `fs-directory` (1, required) | A locally checked-out git repository. The **composition type** — auto-discovers a `git` child (the repo's object database, branches, tags, commits, trees, and remotes) and an `fs-directory` child (the worktree root directory, e.g., `/home/user/projects/my-app`). Most users register this type. The worktree root IS a directory, not a mount point, so git-checkout composes with `fs-directory` directly. | **Physical types — Filesystem layer** (files on disk, used by git checkouts and standalone directories alike): | Type | User Addable | Sandbox | Allowed Parents | Allowed Children | Description | |------|-------------|---------|-----------------|------------------|-------------| | `fs-mount` | yes | `copy_on_write` | (none — always a top-level resource) | `fs-directory` (1, required) | A physical mount point on the local system (e.g., `/mnt/data`, `/home`). Represents the mount itself — the filesystem type (ext4, btrfs, etc.) is a property. The root directory is a required auto-discovered `fs-directory` child. Used for registering entire mount points or storage volumes. | | `fs-directory` | yes | `copy_on_write` | `git-checkout` (0..1, optional), `fs-mount` (0..1, optional), `fs-directory` (0..1, optional) | `fs-directory` (0..*, optional), `fs-file` (0..*, optional), `fs-symlink` (0..*, optional), `fs-hardlink` (0..*, optional) | A directory on the filesystem. Can be the root child of `fs-mount` (mount point root), the worktree root child of `git-checkout`, a subdirectory child of another `fs-directory`, or a standalone user-registered directory. Optionally linked as a child of a `directory` virtual resource when equivalent directory content exists elsewhere. When user-addable, accepts `--path` flag. | | `fs-file` | no | (inherits) | `fs-directory` (1, required) | (none) | A regular file on the local filesystem. Auto-discovered child of `fs-directory`. Optionally linked as a child of a `file` virtual resource when byte-identical content with the same name and permissions exists elsewhere (in another `fs-file` or a `git-tree-entry`). | | `fs-symlink` | no | (inherits) | `fs-directory` (1, required) | (none) | A symbolic link on the local filesystem. Auto-discovered child of `fs-directory`. Optionally linked as a child of a `symlink` virtual resource when a symlink with the same name and target exists elsewhere. | | `fs-hardlink` | no | (inherits) | `fs-directory` (1, required) | (none) | A hard link on the local filesystem — a file with link count > 1. Auto-discovered child of `fs-directory`. The system tracks hard link relationships by inode to avoid treating the same underlying data as distinct resources. | **Virtual types** (abstract identity types that link equivalent physical resources — never user-addable, no sandbox): Virtual types use simple names that mirror their physical counterparts. A virtual resource answers the question: "where else does this same thing exist?" Two physical resources share a virtual parent when they are equivalent by the virtual type's criteria (same content, same name, same permissions, same hash, etc.). | Type | Allowed Children | Description | |------|-----------------|-------------| | `file` | `fs-file` (0..*, optional), `git-tree-entry` (0..*, optional) | Cross-layer file identity. Links physical `fs-file` and `git-tree-entry` resources that represent the same file — identical content bytes, filename, and permissions. Answers: "this file in the working tree and this blob in git's tree are the same file." | | `directory` | `fs-directory` (0..*, optional), `git-tree` (0..*, optional) | Cross-layer directory identity. Links physical `fs-directory` and `git-tree` resources whose recursive contents are equivalent. Answers: "this directory on disk matches this tree object in git." | | `symlink` | `fs-symlink` (0..*, optional), `git-tree-entry` (0..*, optional) | Cross-layer symlink identity. Links physical `fs-symlink` and `git-tree-entry` (mode 120000) resources with the same name and target. | | `commit` | `git-commit` (0..*, optional) | Cross-repo commit identity. Links `git-commit` resources across different repos that share the same commit hash — typically a fork and its upstream. Answers: "this commit exists in both repos." | | `branch` | `git-branch` (0..*, optional) | Cross-repo branch identity. Links `git-branch` resources across different repos with the same branch name and HEAD commit hash. | | `tag` | `git-tag` (0..*, optional) | Cross-repo tag identity. Links `git-tag` resources across different repos with the same tag name and target object. | | `remote` | `git-remote` (0..*, optional) | Cross-repo remote identity. Links `git-remote` resources across different repos that point to the same URL. Answers: "these repos share the same upstream." | | `submodule` | `git-submodule` (0..*, optional) | Cross-repo submodule identity. Links `git-submodule` resources across different repos with the same submodule URL and path. | | `tree` | `git-tree` (0..*, optional) | Cross-repo/cross-commit tree identity. Links `git-tree` resources across different commits or repos that have the same tree hash — identical directory structure and contents. | **Key design notes:** * Virtual types are **never user-addable** — they are created and maintained automatically by the system when equivalent physical resources are detected. * Virtual types have **no sandbox strategy** because they represent abstract identities, not concrete locations. Tools always operate on the physical children. * **Physical types list virtual parents in their Allowed Parents column** (via the "optionally linked" descriptions). For example, an `fs-file` can be linked as a child of a `file` virtual resource when its content matches a `git-tree-entry`. This is how the DAG connects physical and virtual layers. * `git` represents a specific git repository instance — whether hosted remotely (e.g., on GitHub's servers) or locally (a `.git` directory on disk). It is **physical** because it is a concrete manifestation that exists somewhere, not an abstract identity. A `git` resource can be created from a remote URL alone (accessing the object database via the git protocol) or from a local `.git` directory. * `git-checkout` is the type most users register for repos they've cloned. It composes a `git` child (repo metadata) and an `fs-directory` child (the worktree root directory). The worktree root is a directory on an existing filesystem — NOT a separate mount point — so `git-checkout` links directly to `fs-directory`, not `fs-mount`. This means the worktree's files use the **same `fs-directory` and `fs-file` types** regardless of how they were created. * `git-commit` has a `git-tree` child (the root tree object), which in turn has `git-tree-entry` and nested `git-tree` children. This properly models git's internal object structure: commits point to trees, trees contain entries (blobs) and subtrees. * `fs-mount` represents a physical mount point, not a directory. The filesystem type (ext4, btrfs, etc.) is a property on the mount. The root directory is an auto-discovered `fs-directory` child. Use `fs-mount` for registering mount points and storage volumes. Use `fs-directory` (user-addable) for registering arbitrary directories. * `fs-directory` is user-addable, allowing users to register standalone directories (e.g., a build output folder, a config directory) without wrapping them in an `fs-mount`. An `fs-directory` registered standalone or as a child of `fs-mount`/`git-checkout` uses the same type and auto-discovers the same `fs-file`, `fs-symlink`, `fs-hardlink`, and nested `fs-directory` children. * `git-tree-entry` represents a blob in git's tree — a specific file's content at a specific path and mode. The corresponding file on disk (if checked out) is an `fs-file`. The `file` virtual type bridges these two when their content, name, and permissions are identical. * **4 user-addable types**: `git-checkout` (local repo), `git` (remote or local metadata-only), `fs-mount` (mount point), `fs-directory` (arbitrary directory). ##### Built-in Type Hierarchy The parent-child relationships between built-in resource types form three layers — git structure, git checkout (composition), and filesystem — bridged by virtual identity types: ```kroki-plantuml @startuml skinparam defaultFontSize 11 skinparam objectFontSize 11 skinparam packageStyle rectangle package "GIT-CHECKOUT (composition)" as GC #LightBlue { object "git-checkout" as gc { /home/user/projects/myapp } } package "GIT STRUCTURE" as GS #LightYellow { object "git" as git { repo object DB } object "git-remote" as remote { origin } object "git-tag" as tag { v1.0.0 } object "git-submodule" as submod { lib/shared } object "git-branch" as branch { main } object "git-stash" as stash { stash@0 } object "git-commit" as commit { a1b2c3d } object "git-tree" as tree { e8f1... root } object "git-tree-entry" as entry1 { README.md } object "git-tree" as subtree { src/ subtree } object "git-tree-entry" as entry2 { src/app.ts } object "git-tree-entry" as entry3 { src/main.ts } } package "FILESYSTEM (worktree)" as FS #LightGreen { object "fs-directory" as fsroot { worktree root } object "fs-directory" as srcdir { src/ } object "fs-file" as readme { README.md } object "fs-file" as app { app.ts } object "fs-file" as main { main.ts } } package "STANDALONE FS-DIRECTORY" as SFS #Wheat { object "fs-directory" as sfs { /opt/deploy/myapp } object "fs-directory" as ssrcdir { src/ } object "fs-file" as sreadme { README.md } object "fs-file" as sapp { app.ts } object "fs-file" as smain { main.ts } object "fs-symlink" as symlink { link.txt } } package "STANDALONE FS-MOUNT" as SM #LightCoral { object "fs-mount" as mount { /mnt/data } object "fs-directory" as mountroot { root: / } } package "VIRTUAL LAYER (abstract identities)" as VL #Lavender { object "file" as vfile { app.ts @ sha256:9f8e... } object "directory" as vdir { src/ @ merkle:3d4f... } object "commit" as vcommit { a1b2c3d } object "branch" as vbranch { main @ a1b2c3d } object "remote" as vremote { github.com/org/repo } object "tree" as vtree { e8f1...9d2a } } gc --> git gc --> fsroot mount --> mountroot git --> remote git --> tag git --> submod git --> branch git --> stash branch --> commit commit --> tree tree --> entry1 tree --> subtree subtree --> entry2 subtree --> entry3 fsroot --> srcdir fsroot --> readme srcdir --> app srcdir --> main sfs --> ssrcdir sfs --> sreadme sfs --> symlink ssrcdir --> sapp ssrcdir --> smain app ..> vfile : equivalent sapp ..> vfile : equivalent entry2 ..> vfile : equivalent srcdir ..> vdir : equivalent ssrcdir ..> vdir : equivalent subtree ..> vdir : equivalent commit ..> vcommit : equivalent branch ..> vbranch : equivalent remote ..> vremote : equivalent tree ..> vtree : equivalent @enduml ``` **Reading the diagram:** * **Top left**: A `git-checkout` resource decomposes into a `git` child (left — the repository's full structure) and an `fs-directory` child (center — the worktree root directory at `/home/user/projects/myapp`). The worktree root is a directory, not a mount point. * **Git structure** (left column): The `git` child contains `git-remote` (origin), `git-tag` (v1.0.0), `git-submodule` (lib/shared), `git-stash` (stash@{0}), and `git-branch` (main). The branch contains `git-commit` objects, each commit contains a root `git-tree`, and trees contain `git-tree-entry` (blobs) and nested `git-tree` (subtrees). This properly models git's internal object structure. * **Filesystem** (center/right): The worktree's `fs-directory` contains `src/` (an `fs-directory`), `README.md` (an `fs-file`), and files including `fs-symlink` resources. A standalone `fs-directory` (`/opt/deploy/myapp`) with the same structure. A standalone `fs-mount` (`/mnt/data`) with a root `fs-directory` child. * **Virtual layer** (boxed area): Shows how virtual types link equivalent physical resources: - A `file` virtual resource links `fs-file` and `git-tree-entry` resources that have the same content, filename, and permissions — bridging the filesystem and git layers. - A `directory` virtual resource links `fs-directory` and `git-tree` resources with the same recursive content. - A `commit` virtual resource links `git-commit` resources across repos with the same commit hash. - A `branch` virtual resource links `git-branch` resources across repos with the same name and HEAD. - A `remote` virtual resource links `git-remote` resources across repos with the same URL. - A `tree` virtual resource links `git-tree` resources across repos/commits with the same tree hash. **Key separations:** * **`git` vs `git-checkout`**: A `git` resource represents a specific repository instance (local or remote). It can exist without any local directory (created from a remote URL — the repo exists on the remote server). A `git-checkout` always has both a `git` child and an `fs-directory` child — it represents a locally cloned repo with files on disk. * **`git-tree-entry` vs `fs-file`**: A `git-tree-entry` is a blob entry in git's tree (path + content hash + mode). An `fs-file` is a physical file on disk. When a repo is checked out, both exist and typically have matching content — the `file` virtual type links them when content, name, and permissions match. * **`git-tree` vs `fs-directory`**: A `git-tree` is a tree object in git's object database (a directory listing at a commit). An `fs-directory` is a physical directory on disk. The `directory` virtual type links them when their recursive contents match. * **`git-commit` → `git-tree` → `git-tree-entry`**: This chain properly models git internals. Commits point to a root tree, trees contain entries (blobs) and subtrees. The old design skipped the tree level; the current design preserves it. * **`fs-mount` vs `fs-directory`**: An `fs-mount` is a mount point (e.g., `/mnt/data`). The filesystem type (ext4, btrfs, etc.) is a property. The root directory is an `fs-directory` child. An `fs-directory` is a directory — it can be a child of `fs-mount`, `git-checkout`, or another `fs-directory`, or it can be registered standalone by the user. * **`git-checkout` composes with `fs-directory`, not `fs-mount`**: A git checkout's worktree is a directory on an existing filesystem, not a separate mount point. This is why `git-checkout`'s required child is `fs-directory` (the worktree root), not `fs-mount`. * **Virtual types bridge physical equivalents**: `file` bridges `fs-file` + `git-tree-entry` (cross-layer). `directory` bridges `fs-directory` + `git-tree` (cross-layer). `commit`, `branch`, `tag`, `remote`, `submodule`, and `tree` link the same git structural element across different repos. ##### Concrete Example: A Made-Up Project Consider a web application called "Acme Dashboard" with three registered resources: 1. **`local/acme-app`** — a checked-out git repo at `/home/alice/projects/acme-dashboard` (type: `git-checkout`) 2. **`local/acme-upstream`** — a git repo accessed via remote URL, not cloned (type: `git`) 3. **`local/acme-deploy`** — a standalone directory at `/opt/deploy/acme-dashboard` containing a production build snapshot (type: `fs-directory`) **Registration:**

# 1) Checked-out git repo (has local files on disk)
agents resource add git-checkout local/acme-app \
  --path /home/alice/projects/acme-dashboard --branch main

# 2) Git repo via remote URL (no local checkout — metadata only)
agents resource add git local/acme-upstream \
  --url git@github.com:acmecorp/dashboard.git

# 3) Standalone directory (not a git repo — just files on disk)
agents resource add fs-directory local/acme-deploy \
  --path /opt/deploy/acme-dashboard
**What gets auto-discovered for each:** **`local/acme-app`** (type: `git-checkout`) discovers two children — a `git` and an `fs-directory` (the worktree root): ```kroki-plantuml @startwbs * local/acme-app\n(git-checkout / physical) ** local/acme-app:repo\n(git / physical) *** acme-app:repo:origin\n(git-remote)\ngit@github.com:acmecorp/dashboard.git *** acme-app:repo:v1.0.0\n(git-tag) *** acme-app:repo:lib/shared\n(git-submodule @ c4d5e6f) *** acme-app:repo:stash@0\n(git-stash) *** acme-app:repo:main\n(git-branch) **** main:a7f3e21\n(git-commit) ***** main:a7f3e21:tree\n(git-tree / root) ****** a7f3e21:README.md\n(git-tree-entry) ****** a7f3e21:package.json\n(git-tree-entry) ****** a7f3e21:src/\n(git-tree / subtree) ******* a7f3e21:src/app.ts\n(git-tree-entry) ******* a7f3e21:src/api.ts\n(git-tree-entry) ******* a7f3e21:src/utils.ts\n(git-tree-entry) *** acme-app:repo:develop\n(git-branch) **** develop:b2c4d8e\n(git-commit) ***** develop:b2c4d8e:tree\n(git-tree) ****** b2c4d8e:src/\n(git-tree) ******* b2c4d8e:src/app.ts\n(git-tree-entry) ******* b2c4d8e:src/api.ts\n(git-tree-entry / modified) ** local/acme-app:worktree\n(fs-directory / physical)\n/home/alice/projects/acme-dashboard/ *** worktree:src/\n(fs-directory) **** worktree:src/app.ts\n(fs-file) **** worktree:src/api.ts\n(fs-file) **** worktree:src/utils.ts\n(fs-file) *** worktree:package.json\n(fs-file) *** worktree:README.md\n(fs-file) *** worktree:docs -> ../docs\n(fs-symlink) @endwbs ``` The `git-checkout` cleanly separates two concerns: the `git` child contains version control structure (remotes, branches, tags, stashes, submodules, commits, trees, and tree entries — git's full object model), while the `fs-directory` child is the worktree root directory containing the actual files on disk. Note how git's internal structure is fully modeled: `git-commit` → `git-tree` (root tree object) → `git-tree-entry` (blobs) and nested `git-tree` (subtrees). The worktree root is a directory (`fs-directory`), not a mount point — `git-checkout` does not own an `fs-mount` resource because a git checkout's worktree is just a directory on an existing filesystem. When content matches (as it does for a clean checkout), virtual types link the `fs-file` and `git-tree-entry` resources. **`local/acme-upstream`** (type: `git`, remote URL — NOT checked out) discovers: ```kroki-plantuml @startwbs * local/acme-upstream\n(git / physical)\ngit@github.com:acmecorp/dashboard.git ** acme-upstream:origin\n(git-remote) ** acme-upstream:v1.0.0\n(git-tag) ** acme-upstream:main\n(git-branch) *** main:a7f3e21\n(git-commit) **** main:a7f3e21:tree\n(git-tree) ***** a7f3e21:src/\n(git-tree) ****** a7f3e21:src/app.ts\n(git-tree-entry) ****** a7f3e21:src/api.ts\n(git-tree-entry) ** acme-upstream:develop\n(git-branch) *** ...\n(remaining structure) @endwbs ``` A standalone `git` resource has **full access to branches, tags, commits, trees, and tree entries** — everything in the git object database — but **no `fs-directory` child** and **no `fs-file` resources**. There are no files on the local disk (the repo exists on GitHub's servers). Tools that need local file access cannot bind to it. This is the key difference from `git-checkout`: a `git` resource represents a specific repo instance. Plans can reason about history, diffs between branches, remote relationships — without a local clone. A `git-checkout` adds the local worktree directory on top. **`local/acme-deploy`** (type: `fs-directory`, standalone) discovers: ```kroki-plantuml @startwbs * local/acme-deploy\n(fs-directory / physical)\n/opt/deploy/acme-dashboard/ ** acme-deploy:src/\n(fs-directory) *** acme-deploy:src/app.ts\n(fs-file) *** acme-deploy:src/api.ts\n(fs-file) *** acme-deploy:src/utils.ts\n(fs-file) ** acme-deploy:package.json\n(fs-file) ** acme-deploy:README.md\n(fs-file) @endwbs ``` A standalone `fs-directory` — no git metadata, no branches, no commits, no tree entries. Just a directory containing files and subdirectories. Uses the **same `fs-directory` and `fs-file` types** as the git checkout's worktree root. **Virtual resource linking across all three:** After all three resources are registered, the system detects equivalent physical resources and creates virtual parents to link them: ```kroki-plantuml @startuml skinparam defaultFontSize 10 skinparam objectFontSize 10 skinparam packageStyle rectangle left to right direction package "PHYSICAL: local/acme-app" as P1 #LightBlue { object "fs-directory" as acmeWtSrc { worktree:src/ } object "fs-file" as acmeWtApp { worktree:src/app.ts } object "fs-file" as acmeWtUtils { worktree:src/utils.ts } object "fs-file" as acmeWtApi { worktree:src/api.ts } object "git-tree" as acmeGitSrc { main:a7f3e21:src/ } object "git-tree-entry" as acmeGitApp { main:src/app.ts } object "git-tree-entry" as acmeGitUtils { main:src/utils.ts } object "git-tree-entry" as acmeGitApi { main:src/api.ts } object "git-commit" as acmeCommit { main:a7f3e21 } object "git-branch" as acmeBranch { main } object "git-tag" as acmeTag { v1.0.0 } object "git-remote" as acmeRemote { origin } object "git-tree" as acmeTree { main:a7f3e21:tree } object "git-submodule" as acmeSubmod { lib/shared } } package "PHYSICAL: local/acme-deploy" as P2 #LightGreen { object "fs-directory" as deploySrc { src/ } object "fs-file" as deployApp { src/app.ts } object "fs-file" as deployUtils { src/utils.ts } object "fs-file" as deployApi { src/api.ts } } package "PHYSICAL: local/acme-upstream" as P3 #LightYellow { object "git-tree" as upstreamSrc { main:a7f3e21:src/ } object "git-tree-entry" as upstreamApp { main:src/app.ts } object "git-tree-entry" as upstreamUtils { main:src/utils.ts } object "git-tree-entry" as upstreamApi { main:src/api.ts } object "git-commit" as upstreamCommit { main:a7f3e21 } object "git-branch" as upstreamBranch { main } object "git-tag" as upstreamTag { v1.0.0 } object "git-remote" as upstreamRemote { origin } object "git-tree" as upstreamTree { main:a7f3e21:tree } } package "VIRTUAL LAYER\n(auto-created by equivalence)" as VL #Lavender { object "directory" as vDir { src/ (merkle:3d4f...) } object "file" as vAppTs { app.ts (sha256:9f8e...) } object "file" as vUtilsTs { utils.ts (sha256:a2b1...) } object "file" as vApiTs { api.ts (sha256:e1d3...) } object "commit" as vCommit { a7f3e21 } object "branch" as vBranch { main @ a7f3e21 } object "tag" as vTag { v1.0.0 } object "remote" as vRemote { github.com:acmecorp/dashboard.git } object "tree" as vTree { e8f1...9d2a } object "submodule" as vSubmod { lib/shared } } acmeWtSrc ..> vDir deploySrc ..> vDir acmeGitSrc ..> vDir upstreamSrc ..> vDir acmeWtApp ..> vAppTs deployApp ..> vAppTs acmeGitApp ..> vAppTs upstreamApp ..> vAppTs acmeWtUtils ..> vUtilsTs deployUtils ..> vUtilsTs acmeGitUtils ..> vUtilsTs upstreamUtils ..> vUtilsTs acmeWtApi ..> vApiTs deployApi ..> vApiTs acmeGitApi ..> vApiTs upstreamApi ..> vApiTs note "NOT linked: develop:b2c4d8e:src/api.ts\n(different content on develop branch)" as N1 acmeCommit ..> vCommit upstreamCommit ..> vCommit acmeBranch ..> vBranch upstreamBranch ..> vBranch acmeTag ..> vTag upstreamTag ..> vTag acmeRemote ..> vRemote upstreamRemote ..> vRemote acmeTree ..> vTree upstreamTree ..> vTree acmeSubmod ..> vSubmod @enduml ``` **How the `directory` virtual type works:** The `directory` virtual resource for `src/` exists because the `src/` directory has identical recursive content across multiple physical locations. Its children include both `fs-directory` resources (physical directories on disk) and `git-tree` resources (tree objects in git's object database). The system detects directory equivalence by computing a Merkle hash over the sorted child content hashes. If someone adds a file to the deploy directory but not the git checkout's worktree, the Merkle hashes diverge, and `local/acme-deploy:src/` is unlinked from the `directory` virtual parent. **How the `file` virtual type works:** The `file` virtual type links physical resources that represent the same file — identical content bytes, filename, and permissions. It bridges across layers: an `fs-file` on disk and a `git-tree-entry` in git's tree are linked when they have matching content. This is the primary cross-layer bridge. When content diverges (e.g., an uncommitted edit), the virtual link is broken. **How `commit` works across repos:** When two `git` resources share history (e.g., a fork and its upstream), the same commit hash will appear in both repos' branches. The `commit` virtual type links these — the commit object `a7f3e21` in the local repo and `a7f3e21` in the upstream repo are the same commit, and the virtual parent captures this identity. **Divergence scenario:** If a developer edits `src/api.ts` in the deploy directory (`/opt/deploy/acme-dashboard/src/api.ts`), the system detects the content hash change and: 1. Unlinks `local/acme-deploy:src/api.ts` from `file: api.ts (sha256:e1d3...8a9b)` (content no longer matches). 2. Unlinks `local/acme-deploy:src/` from `directory: src/ (merkle:3d4f...a2b1)` (directory contents no longer identical — the Merkle hash has changed). 3. The git checkout's worktree files and git-tree-entry resources remain linked (their content hasn't changed). 4. If the edit makes the deploy file match some *other* known content hash, a new virtual link may be created. Additional resource types (databases, APIs, cloud infrastructure, etc.) can be added as **custom resource types** via `agents resource type add`. ##### Custom Resource Types Custom resource types are defined in YAML configuration files and registered via `agents resource type add`. Once registered, a custom type automatically becomes available as a new subcommand under `agents resource add`.

# File: resource-types/database.yaml
cleveragents:
  version: "3.0"

resource_type:
  name: local/database
  description: "A SQL database (PostgreSQL, MySQL, SQLite, etc.)"
  physical_or_virtual: physical
  user_addable: true

  # CLI arguments for `agents resource add local/database`
  cli_arguments:
    - name: connection-string
      type: string
      required: true
      description: "Database connection string (e.g., postgresql://host/dbname)"
      validation:
        pattern: "^(postgresql|mysql|sqlite)://"
    - name: schema
      type: string
      required: false
      description: "Default schema to use"
    - name: read-only
      type: boolean
      required: false
      default: false
      description: "Whether the database should be treated as read-only"

  # Sandbox and handler
  sandbox_strategy: transaction_rollback
  handler: DatabaseHandler
  checkpointable: true

  # Allowed parent types (empty means can be top-level)
  allowed_parent_types: []

  # Child types
  child_types:
    - type: local/db-schema
      auto_discover: true
      manual_link: false
      description: "Discovered database schemas"
    - type: local/db-table
      auto_discover: true
      manual_link: false
      description: "Discovered tables within schemas"

  # Capabilities
  capabilities:
    readable: true
    writable: true
    sandboxable: true
    checkpointable: true
When this type is registered:

agents resource type add --config ./resource-types/database.yaml local/database
# Now available: agents resource add local/database <NAME> --connection-string CONN [--schema SCHEMA] [--read-only]
The `user_addable` field determines whether the type appears as a subcommand. Types with `user_addable: false` are only auto-generated as children — for example, `git-remote`, `git-branch`, `git-commit`, and `git-tree-entry` are never created directly by users but are discovered when a `git` (or `git-checkout`) resource is registered. #### The Resource DAG Resources form a **directed acyclic graph** (DAG), not a simple tree. A resource can have **multiple parents** and **multiple children**, subject to type constraints. The diagram below shows how a `git-checkout`, a standalone `git` repo (remote), a standalone `fs-directory`, and virtual resources interconnect: ```kroki-plantuml @startuml skinparam defaultFontSize 10 skinparam objectFontSize 10 skinparam packageStyle rectangle package "GIT-CHECKOUT: local/app" as GCO #LightBlue { object "git-checkout" as gco { local/app } object "git" as gcoGit { local/app:repo } object "git-remote" as gcoRemote { origin } object "git-tag" as gcoTag { v1.0.0 } object "git-branch" as gcoBranch { main } object "git-commit" as gcoCommit { a1b2c3d } object "git-tree" as gcoTree { e8f1... root } object "git-tree-entry" as gcoEntry1 { README.md } object "git-tree" as gcoSubtree { src/ } object "git-tree-entry" as gcoEntry2 { src/app.ts } object "git-tree-entry" as gcoEntry3 { src/main.ts } object "fs-directory" as gcoFs { local/app:worktree } object "fs-directory" as gcoSrcDir { src/ } object "fs-file" as gcoReadme { README.md } object "fs-file" as gcoApp { app.ts } object "fs-file" as gcoMain { main.ts } } package "GIT (remote): local/upstream" as GR #LightYellow { object "git" as gr { local/upstream } object "git-remote" as grRemote { origin } object "git-tag" as grTag { v1.0.0 } object "git-branch" as grBranch { main } object "git-commit" as grCommit { a1b2c3d } object "git-tree" as grTree { e8f1... root } object "git-tree-entry" as grEntry1 { README.md } object "git-tree" as grSubtree { src/ } object "git-tree-entry" as grEntry2 { src/app.ts } object "git-tree-entry" as grEntry3 { src/main.ts } } package "STANDALONE FS-DIRECTORY: local/deploy" as SFD #LightGreen { object "fs-directory" as sfd { /opt/deploy/myapp } object "fs-directory" as sfdSrcDir { src/ } object "fs-file" as sfdReadme { README.md } object "fs-file" as sfdApp { app.ts } object "fs-file" as sfdMain { main.ts } } package "VIRTUAL LAYER" as VL #Lavender { object "file" as vFile { app.ts@v1 } object "directory" as vDir { src/@v1 } object "commit" as vCommit { a1b2c3d } object "branch" as vBranch { main@a1b... } object "tag" as vTag { v1.0.0 } object "remote" as vRemote { github.com/org/repo } object "tree" as vTree { e8f1...9d2a } } ' Physical hierarchy gco --> gcoGit gco --> gcoFs gcoGit --> gcoRemote gcoGit --> gcoTag gcoGit --> gcoBranch gcoBranch --> gcoCommit gcoCommit --> gcoTree gcoTree --> gcoEntry1 gcoTree --> gcoSubtree gcoSubtree --> gcoEntry2 gcoSubtree --> gcoEntry3 gcoFs --> gcoSrcDir gcoFs --> gcoReadme gcoSrcDir --> gcoApp gcoSrcDir --> gcoMain gr --> grRemote gr --> grTag gr --> grBranch grBranch --> grCommit grCommit --> grTree grTree --> grEntry1 grTree --> grSubtree grSubtree --> grEntry2 grSubtree --> grEntry3 sfd --> sfdSrcDir sfd --> sfdReadme sfdSrcDir --> sfdApp sfdSrcDir --> sfdMain ' Virtual equivalence links gcoApp ..> vFile sfdApp ..> vFile gcoEntry2 ..> vFile grEntry2 ..> vFile gcoSrcDir ..> vDir sfdSrcDir ..> vDir gcoSubtree ..> vDir grSubtree ..> vDir gcoCommit ..> vCommit grCommit ..> vCommit gcoBranch ..> vBranch grBranch ..> vBranch gcoTag ..> vTag grTag ..> vTag gcoRemote ..> vRemote grRemote ..> vRemote gcoTree ..> vTree grTree ..> vTree @enduml ``` **Key properties of the DAG:** 1. **Multiple parents**: A `git-tree-entry` has a `git-tree` parent (git structure) and potentially a `file` virtual parent (content identity). An `fs-file` has an `fs-directory` parent (filesystem hierarchy) and potentially a `file` virtual parent. An `fs-directory` can be a child of `git-checkout` (as its worktree root), a child of `fs-mount` (as the mount root), a child of another `fs-directory` (as a subdirectory), a child of a `directory` virtual (identity), or a standalone user-registered resource. 2. **Multiple children**: A `git-checkout` has a `git` child and an `fs-directory` child. A `git` resource has `git-remote`, `git-branch`, `git-tag`, `git-stash`, `git-submodule`, and `git-commit` children. A `git-commit` has a `git-tree` child. A `git-tree` has `git-tree-entry` and nested `git-tree` children. 3. **Cycles are forbidden**: The graph is always a DAG. The system validates this when links are created. 4. **Type constraints**: Not any resource can be a child of any other — the parent's resource type defines which child types are allowed (see the Allowed Parents / Allowed Children columns in the built-in types tables). 5. **Cross-layer bridge**: Virtual resources link equivalent physical resources from different layers (git structure, filesystem, different repos). The `file` virtual type bridges `fs-file` + `git-tree-entry`. The `directory` virtual type bridges `fs-directory` + `git-tree`. The `commit`, `branch`, `tag`, `remote`, `submodule`, and `tree` virtual types link the same git structural element across different repositories. #### Physical vs Virtual Resources Resources are either **physical** or **virtual**, a distinction determined by their resource type. **Physical resources** are specific, concrete manifestations. Each physical resource is a particular instance that exists somewhere — a file at a particular path on a particular machine, a git repository at a particular URL on a particular server, a commit in a particular repo. Physical resources can be directly read and written by tools. Most resources are physical. **Virtual resources** represent an abstract identity that links equivalent physical resources. They answer the question: "where else does this same thing exist?" A `file` virtual resource links all the physical `fs-file` and `git-tree-entry` resources that have the same content, filename, and permissions. A `commit` virtual resource links `git-commit` resources across repos that share the same commit hash. Virtual resources have no location of their own and cannot be directly read or written. **Rules for the physical/virtual boundary:** 1. The **children of a virtual resource** can be physical resources, other virtual resources, or a combination of both. 2. A **physical resource's parents** can be either physical or virtual resources. 3. **Not all physical resources need a virtual parent**. Virtual resource linking is optional and only applies when equivalence tracking is meaningful. **Equivalence linking:** Two physical resources share a virtual parent when they are equivalent by the virtual type's criteria. Each virtual type has its own equivalence semantics: * **`file`**: Physical files (`fs-file` or `git-tree-entry`) share a `file` parent when they have the same content bytes (SHA-256), the same filename, and the same permissions. This is the primary cross-layer bridge — it links filesystem files with git blob entries. Example: `local/app:worktree:src/main.py` (an `fs-file`) and `local/app:repo:main:a1b...:src/main.py` (a `git-tree-entry`) both have the same content in a clean checkout, so they share a `file` virtual parent. * **`directory`**: Physical directories (`fs-directory`) and git trees (`git-tree`) share a `directory` parent when their full recursive contents are equivalent. This bridges the filesystem and git layers — the `src/` directory on disk and the `src/` subtree in git's tree object can be linked when their contents match. * **`symlink`**: Physical symlinks (`fs-symlink`) and git tree entries with symlink mode (`git-tree-entry` mode 120000) share a `symlink` parent when they have the same name and target. * **`commit`**: Physical commits (`git-commit`) in different repos share a `commit` parent when they have the same commit hash. This is common when repos share history (e.g., a fork and its upstream). * **`branch`**: Physical branches (`git-branch`) in different repos share a `branch` parent when they have the same name and the same HEAD commit hash. A push or local commit on one repo causes divergence. * **`tag`**: Physical tags (`git-tag`) in different repos share a `tag` parent when they have the same tag name and target object. * **`remote`**: Physical remotes (`git-remote`) in different repos share a `remote` parent when they point to the same URL. Answers: "these repos share the same upstream." * **`submodule`**: Physical submodules (`git-submodule`) in different repos share a `submodule` parent when they have the same submodule URL and path. * **`tree`**: Physical tree objects (`git-tree`) in different commits or repos share a `tree` parent when they have the same tree hash — identical directory structure and contents. **Divergence detection:** When a physical resource's content changes (e.g., a file is edited, a directory gains a new file, a branch advances), the system detects that it may no longer match its virtual parent's identity. At that point: * If the edit causes the physical resource to diverge from all other physical siblings under the same virtual parent, the physical resource is unlinked from that virtual parent. * If the edit makes it match a *different* virtual resource's identity, it may be re-linked. * Content equivalence is tracked via content hashing: SHA-256 for files, Merkle hashes for directories, tree hashes for git trees, commit hashes for commits, HEAD+name for branches, name+target for tags, URL for remotes. * **Cascading divergence**: When a `file` link breaks, the parent `directory` virtual resource is also re-evaluated (since its identity depends on all child identities). This enables powerful queries like: * "What physical locations exist for this file content?" → Find all physical resources sharing the `file` virtual parent. * "What tools can edit this file?" → Find all tools with resource bindings compatible with the physical resource types. * "Has this file been modified in any location?" → Check if all physical siblings still share the same `file` virtual parent. * "Are these two repos in sync?" → Check if their branches share a `branch` virtual parent. * "Which directories are identical across deployments?" → Find `directory` virtual resources with multiple physical children. * "What submodules are shared across projects?" → Find `submodule` virtual resources with multiple physical children. #### Resource Registration (CLI) Resources are created via `agents resource add ` with type-specific arguments. Auto-discovered children are created automatically:

# Register a checked-out git repository (most common)
agents resource add git-checkout local/api-repo --path /home/user/projects/api-service --branch main
# Auto-discovers: git child (repo metadata, remotes, branches, commits, tree entries)
#                 + fs-directory child (worktree root directory, subdirectories, files)

# Register a git repo via remote URL (no local checkout — exists on remote server)
agents resource add git local/upstream --url git@github.com:org/upstream.git
# Auto-discovers: remotes, branches, tags, commits, trees, tree entries, stashes, submodules
#                 (no fs-directory — not checked out locally)

# Register a standalone directory (not a git repo — just files on disk)
agents resource add fs-directory local/docs --path /opt/docs/api-reference
# Auto-discovers: subdirectories, files, symlinks, hardlinks

# Register a standalone filesystem mount (entire volume)
agents resource add fs-mount local/data-volume --mount-path /mnt/data
# Auto-discovers: root fs-directory, subdirectories, files, symlinks, hardlinks

# Link resources to projects (resources must be registered first)
agents project link-resource local/api-service local/api-repo
agents project link-resource local/api-service local/docs --read-only
#### Auto-Discovery When a resource is registered, its resource type's handler **auto-discovers child resources**. This process: 1. **Scans** the resource to identify children (e.g., a `git-checkout` handler creates a `git` child and an `fs-directory` child for the worktree root; a `git` handler lists remotes, branches, tags, commits, stashes, and submodules; a `git-commit` handler creates a root `git-tree` child; a `git-tree` handler lists tree entries and subtrees; an `fs-mount` handler creates a root `fs-directory` and discovers its contents; an `fs-directory` handler discovers subdirectories, files, symlinks, and hardlinks). 2. **Creates** child resource records in the Resource Registry with auto-generated names (e.g., `local/api-repo:repo` for the git child, `local/api-repo:worktree` for the worktree root `fs-directory` child, `local/api-repo:repo:main` for a branch). 3. **Reuses existing resources**: If a discovered child matches an already-registered resource (same type + same value/location), the existing resource is **linked** as a child rather than creating a duplicate. For example, if `local/docs` (an `fs-directory` resource at `/home/user/projects/api-service`) is already registered and the `git-checkout` handler discovers its worktree root at the same path, it links to the existing `local/docs` resource instead of creating a new one. 4. **Links virtual resources**: When auto-discovery detects that a physical resource's content matches an existing virtual resource (via content hashing), it links the physical resource as a child of the virtual resource. Auto-discovered children are marked as `auto: true` in the DAG, distinguishing them from manually linked children. Auto-discovered links cannot be manually unlinked (they are managed by the handler), while manually created links can be freely managed. Auto-discovery runs: * At registration time (`agents resource add`) * On refresh (when the system detects changes, e.g., new commits, new files) * On demand (when a tool accesses the resource and the handler detects staleness) #### Resource Capabilities Each resource declares its capabilities, derived from its resource type: | Capability | Description | |-----------|-------------| | `readable` | Whether the resource can be read | | `writable` | Whether the resource can be modified | | `sandboxable` | Whether the resource supports sandbox isolation | | `checkpointable` | Whether the resource supports checkpoint/rollback | These capabilities are used by the **tool execution flow** to validate that a tool's resource binding is compatible — a tool declaring `access: read_write` on a resource slot cannot be bound to a read-only resource. #### Resource Registry CleverAgents maintains a **Resource Registry** — a persistent catalog of all registered resources and their DAG relationships: ```kroki-plantuml @startuml skinparam classAttributeIconSize 0 skinparam classFontSize 13 skinparam defaultFontSize 12 class ResourceRegistry { - resourceIndex : Map - typeIndex : Map -- + add(type, name, properties) : ResourceRecord + update(name, properties) : ResourceRecord + remove(name) : void + lookup(name) : ResourceRecord + list(filters) : ResourceRecord[] + tree(name, depth) : DAG_subtree + link_child(parent, child) : void + unlink_child(parent, child) : void + refresh(name) : void + find_by_content(hash) : ResourceRecord[] + find_virtual_parent(resource) : ResourceRecord } class ResourceRecord { + name : String + type : String + physical_or_virtual : PhysVirt + properties : Map + capabilities : Capabilities + parents : List + children : List + content_hash : String + linked_projects : List + created_at : DateTime + updated_at : DateTime } class ResourceTypeRecord { + name : String + description : String + source : String + physical_or_virtual : PhysVirt + user_addable : Boolean + cli_arguments : List + allowed_parent_types : List + child_types : Map + sandbox_strategy : String + handler : String + capabilities : Capabilities + config_path : String } enum PhysVirt { physical virtual } ResourceRegistry "1" *-- "0..*" ResourceRecord : indexes resources > ResourceRegistry "1" *-- "0..*" ResourceTypeRecord : indexes types > ResourceRecord --> PhysVirt ResourceTypeRecord --> PhysVirt note right of ResourceRegistry **Populated by:** - agents resource add CLI - Auto-discovery during registration - Content-identity linking **Consumed by:** - Tool binding resolution - Sandbox creation - Change tracking - Project linking - Plan validation end note @enduml ``` The Resource Registry persists in the database (local SQLite or server). It works alongside the Tool Registry and Skill Registry. #### Resource Sandbox Strategy **Each resource defines its own sandbox strategy**, determined by its resource type. This is critical because: 1. The same resource may be accessed through different tools and skills 2. Different resource types require different sandboxing approaches 3. Some resources cannot be sandboxed at all | Resource Type | Sandbox Strategy | Rollback Mechanism | |--------------|------------------|-------------------| | `git-checkout` | `git_worktree` | Git reset/checkout | | `git` | `none` | N/A (represents a repo instance — not directly sandboxable) | | `fs-mount` | `copy_on_write` or `overlay` | Restore from snapshot | | `fs-directory` | `copy_on_write` | Restore from snapshot | | Custom database types | `transaction_rollback` | Transaction rollback | | Custom API types | `none` (often not sandboxable) | N/A | The sandbox strategy is inherited by child resources from their parent unless the child type defines its own. For example, `git-branch`, `git-commit`, `git-tree`, and `git-tree-entry` all inherit from their `git` ancestor. `fs-file`, `fs-symlink`, and `fs-hardlink` inherit from their `fs-directory` parent. #### Lazy Sandboxing Resources are **sandboxed lazily when accessed**, not upfront. This is different from indexing — resources are indexed immediately when registered, but sandboxes are only created when execution needs to modify a resource: 1. A project may link many resources (e.g., git repo + databases + cloud accounts) 2. A plan may only need to modify one resource 3. Only the accessed resources are sandboxed 4. Each plan/child plan has its own sandbox containing only edited resources This is efficient for large projects where most resources remain untouched. #### Resource Access Tracking The system tracks **which tools access which resources** through the tool-resource binding system. This tracking happens at multiple levels: 1. **Declaration-time**: Tool YAML declares resource slots with type and access mode (see **Resource Bindings** in the Tools section). 2. **Activation-time**: When a tool is activated for a plan, its resource slots are bound to specific resources. The system records these bindings. 3. **Execution-time**: Every tool invocation logs which bound resources were actually accessed, what operations were performed (read/write/delete), and what paths or objects were touched. This enables: * **Accurate sandbox scoping**: Only sandbox resources that will actually be modified. * **Rollback feasibility analysis**: Know exactly which resources were modified and whether they support rollback. * **Security auditing**: Complete record of which tools accessed which resources and how. * **Cross-resource impact analysis**: Determine if changes to a resource affect tools bound to sibling or child resources. * **Virtual resource consistency**: Detect when a physical resource diverges from its virtual parent. #### Unified Resource Abstraction Layer CleverAgents provides a unified abstraction that allows tools to work with any resource type through a consistent interface. This enables: 1. **Resource-agnostic tools**: A tool like `read_content(path)` works whether the path refers to a file, database record, or API endpoint. 2. **Consistent sandbox semantics**: All resources support the same sandbox lifecycle (create, read, write, checkpoint, rollback). 3. **Pluggable resource handlers**: New resource types can be added by registering custom resource types without modifying existing tools. 4. **Unified change tracking**: All resource modifications flow into the same ChangeSet model. ##### Resource Handler Interface Every resource type provides a handler that implements this interface:

class ResourceHandler(Protocol):
    """Handler for a specific resource type."""
    
    def read(self, path: str, sandbox: Sandbox) -> Content:
        """Read content from the sandboxed resource."""
        ...
    
    def write(self, path: str, content: Content, sandbox: Sandbox) -> Change:
        """Write content and return the Change record."""
        ...
    
    def delete(self, path: str, sandbox: Sandbox) -> Change:
        """Delete resource and return the Change record."""
        ...
    
    def list(self, pattern: str, sandbox: Sandbox) -> list[str]:
        """List paths matching pattern."""
        ...
    
    def diff(self, path: str, sandbox: Sandbox) -> str:
        """Generate diff between sandbox and original state."""
        ...
    
    def supports_operation(self, operation: OperationType) -> bool:
        """Check if this resource supports the given operation."""
        ...
    
    def discover_children(self, resource: ResourceRecord) -> list[ResourceRecord]:
        """Auto-discover child resources (called at registration and refresh)."""
        ...
    
    def content_hash(self, path: str, sandbox: Sandbox) -> str:
        """Compute content hash for identity tracking."""
        ...
    
    def create_sandbox(self, resource: ResourceRecord) -> Sandbox:
        """Create a sandbox for this resource using its type's strategy."""
        ...
    
    def create_checkpoint(self, sandbox: Sandbox) -> Checkpoint:
        """Create a checkpoint within the sandbox."""
        ...
    
    def rollback_to(self, sandbox: Sandbox, checkpoint: Checkpoint) -> None:
        """Roll back sandbox state to a checkpoint."""
        ...
##### Built-in Resource Handlers | Resource Type | Handler | Read | Write | Delete | Sandbox Strategy | |--------------|---------|------|-------|--------|------------------| | `git-checkout` | `GitCheckoutHandler` | ✓ | ✓ | ✓ | `git_worktree` | | `git` | `GitHandler` | ✓ | ✗ | ✗ | `none` | | `git-commit`, `git-tree`, `git-tree-entry` | `GitObjectHandler` | ✓ | ✗ | ✗ | (inherits) | | `git-branch`, `git-tag`, `git-stash` | `GitRefHandler` | ✓ | ✓ | ✓ | (inherits) | | `git-remote`, `git-submodule` | `GitConfigHandler` | ✓ | ✗ | ✗ | (inherits) | | `fs-mount` | `FilesystemHandler` | ✓ | ✓ | ✓ | `copy_on_write` | | `fs-directory` | `FilesystemHandler` | ✓ | ✓ | ✓ | `copy_on_write` | | `fs-file`, `fs-symlink`, `fs-hardlink` | `FilesystemHandler` | ✓ | ✓ | ✓ | (inherits) | Additional handlers are provided by custom resource types when they are registered. ##### Resource Path Resolution Paths in tool invocations are resolved through a resource routing system that uses tool-resource bindings to determine the target resource: ```kroki-mermaid sequenceDiagram participant Tool as Tool Invocation participant Router as Resource Router participant Handler as GitCheckoutHandler Tool->>Router: edit_file(path='src/main.py', ...) Router->>Router: Check tool's resource bindings Note right of Router: slot 'repo' bound to
local/api-repo (git-checkout) Router->>Router: Resolve path within bound resource Note right of Router: local/api-repo:worktree:src/main.py Router->>Handler: Route to resource handler Handler->>Handler: Resolve path to sandbox worktree Handler->>Handler: Operate on sandboxed state Handler-->>Tool: Return Change record ``` When a tool has multiple resource slots bound, the path scheme or slot name disambiguates:

# Explicit slot reference
path://repo/src/main.py      → routes to the "repo" slot's bound resource
path://docs/api/readme.md    → routes to the "docs" slot's bound resource

# Default: unqualified paths route to the tool's primary resource slot
src/main.py                  → routes to the first (or only) resource slot
### Code Intelligence & Context Discovery #### Overview CleverAgents employs a sophisticated multi-layered indexing and discovery system that enables agents to efficiently navigate and understand codebases of any scale. This system goes far beyond simple text search, providing semantic understanding of code structure, dependencies, and relationships through a combination of indexed embeddings, vector search, and an RDF-based graph store. **Critical Design Decision**: All indexing happens immediately when resources are added to projects or when code changes. There is no "on-demand" indexing during agent execution. This ensures that agents always have instant access to search capabilities without any indexing delays. The computational cost is paid once upfront, not repeatedly during agent operations. **Key Design Principles:** 1. **Pluggable Architecture**: Every component can be extended or replaced 2. **Progressive Enhancement**: System works with basic text search, enhances with advanced features 3. **Eager Indexing**: Indices are built immediately when resources are added and kept continuously up-to-date 4. **Agent Awareness**: Agents understand available indices through skills 5. **Real-time Synchronization**: Indices update immediately as code changes #### Architecture Components ##### 1. Multi-Modal Indexing Engine The indexing engine operates across three complementary modalities:

IndexingEngine:
  modalities:
    # Traditional text-based indexing
    text_index:
      type: "full_text_search"
      backend: "tantivy" | "elasticsearch" | "sqlite_fts"
      features:
        - Token-based search
        - Regex patterns
        - Language-aware tokenization
        - File path indexing
    
    # Semantic understanding via embeddings
    vector_index:
      type: "embedding_search"
      backend: "faiss" | "qdrant" | "weaviate" | "pgvector"
      models:
        - code: "codegen-6B-multi"
        - docs: "instructor-xl"
        - cross-modal: "clip-code"
      features:
        - Function-level embeddings
        - Class-level embeddings
        - Module-level embeddings
        - Documentation embeddings
        - Cross-language similarity
    
    # Structural understanding via graph
    graph_index:
      type: "rdf_knowledge_graph"
      backend: "blazegraph" | "stardog" | "apache_jena" | "neo4j"
      ontology: "CodeOntology"
      features:
        - AST-based relationships
        - Dependency graphs
        - Call graphs
        - Inheritance hierarchies
        - Data flow analysis
##### 2. RDF-Based Code Knowledge Graph The graph store represents code as a rich semantic network using RDF (Resource Description Framework) triples. This enables sophisticated queries about code structure and relationships. **Core Ontology Design:**

# CodeOntology - Core vocabulary for code representation
@prefix code: <https://cleveragents.ai/ontology/code#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

# Core Classes
code:Module a rdfs:Class ;
    rdfs:comment "A code module (file, package, namespace)" .

code:Class a rdfs:Class ;
    rdfs:comment "A class or similar construct" .

code:Function a rdfs:Class ;
    rdfs:comment "A function, method, or procedure" .

code:Variable a rdfs:Class ;
    rdfs:comment "A variable, constant, or field" .

code:Type a rdfs:Class ;
    rdfs:comment "A type definition" .

# Core Properties
code:contains a rdf:Property ;
    rdfs:domain code:Module ;
    rdfs:range code:Entity ;
    rdfs:comment "Module contains entity" .

code:imports a rdf:Property ;
    rdfs:domain code:Module ;
    rdfs:range code:Module ;
    rdfs:comment "Module imports another module" .

code:extends a rdf:Property ;
    rdfs:domain code:Class ;
    rdfs:range code:Class ;
    rdfs:comment "Class inheritance relationship" .

code:calls a rdf:Property ;
    rdfs:domain code:Function ;
    rdfs:range code:Function ;
    rdfs:comment "Function calls another function" .

code:references a rdf:Property ;
    rdfs:domain code:Entity ;
    rdfs:range code:Entity ;
    rdfs:comment "Entity references another entity" .

code:hasParameter a rdf:Property ;
    rdfs:domain code:Function ;
    rdfs:range code:Parameter ;
    rdfs:comment "Function has parameter" .

code:returns a rdf:Property ;
    rdfs:domain code:Function ;
    rdfs:range code:Type ;
    rdfs:comment "Function return type" .

# Annotations
code:hasDocstring a rdf:Property ;
    rdfs:domain code:Entity ;
    rdfs:range xsd:string .

code:hasComplexity a rdf:Property ;
    rdfs:domain code:Function ;
    rdfs:range xsd:integer ;
    rdfs:comment "Cyclomatic complexity" .

code:hasTestCoverage a rdf:Property ;
    rdfs:domain code:Entity ;
    rdfs:range xsd:decimal ;
    rdfs:comment "Test coverage percentage" .
**Example Knowledge Graph Fragment:**

# Concrete example: Authentication module
<file:///src/auth/auth_manager.py> a code:Module ;
    code:imports <file:///src/core/user.py> ;
    code:imports <file:///src/utils/crypto.py> ;
    code:contains <class://AuthManager> .

<class://AuthManager> a code:Class ;
    code:hasMethod <method://AuthManager.authenticate> ;
    code:hasMethod <method://AuthManager.validate_token> ;
    code:extends <class://BaseManager> ;
    code:hasDocstring "Manages user authentication and session tokens" .

<method://AuthManager.authenticate> a code:Function ;
    code:hasParameter <param://username> ;
    code:hasParameter <param://password> ;
    code:returns <type://AuthToken> ;
    code:calls <method://CryptoUtils.hash_password> ;
    code:calls <method://UserDB.find_user> ;
    code:hasComplexity 8 ;
    code:hasTestCoverage 0.95 .
**Advanced Graph Queries:**

# Find all functions that manipulate user authentication
PREFIX code: <https://cleveragents.ai/ontology/code#>
SELECT ?function ?module
WHERE {
    ?function a code:Function ;
              code:calls*/code:references ?entity .
    ?entity rdfs:label ?label .
    FILTER(CONTAINS(LCASE(?label), "auth") || CONTAINS(LCASE(?label), "user"))
    ?module code:contains ?function .
}

# Find circular dependencies
SELECT ?module1 ?module2
WHERE {
    ?module1 code:imports+ ?module2 .
    ?module2 code:imports+ ?module1 .
    FILTER(?module1 != ?module2)
}

# Find most complex untested functions
SELECT ?function ?complexity
WHERE {
    ?function a code:Function ;
              code:hasComplexity ?complexity ;
              code:hasTestCoverage ?coverage .
    FILTER(?complexity > 10 && ?coverage < 0.5)
}
ORDER BY DESC(?complexity)
LIMIT 10
##### 3. Intelligent Context Assembly Pipeline The context assembly pipeline leverages all three indices to build optimal context for each agent:

class ContextAssemblyPipeline:
    def assemble_context(self, 
                        query: str, 
                        actor_type: str,
                        resource_scope: List[Resource],
                        max_tokens: int) -> Context:
        
        # Stage 1: Query Understanding
        intent = self.analyze_query_intent(query)
        entities = self.extract_entities(query)  # Classes, functions, concepts
        
        # Stage 2: Multi-Modal Search
        results = SearchResults()
        
        # Text search for exact matches
        if intent.needs_exact_match:
            text_results = self.text_index.search(
                query=query,
                filters={"resources": resource_scope},
                limit=100
            )
            results.add(text_results)
        
        # Vector search for semantic similarity
        if intent.needs_semantic_match:
            query_embedding = self.embed_query(query, actor_type)
            vector_results = self.vector_index.search(
                embedding=query_embedding,
                filters={"resources": resource_scope},
                limit=50
            )
            results.add(vector_results)
        
        # Graph traversal for structural relationships
        if entities:
            graph_results = self.graph_index.traverse(
                start_nodes=entities,
                patterns=self.get_patterns_for_actor(actor_type),
                max_depth=3,
                limit=50
            )
            results.add(graph_results)
        
        # Stage 3: Relevance Ranking
        ranked_results = self.rank_by_relevance(
            results=results,
            actor_type=actor_type,
            query_intent=intent
        )
        
        # Stage 4: Context Optimization
        context = self.optimize_context(
            ranked_results=ranked_results,
            max_tokens=max_tokens,
            strategy=self.get_strategy_for_actor(actor_type)
        )
        
        return context
    
    def get_patterns_for_actor(self, actor_type: str) -> List[GraphPattern]:
        """Different actors need different traversal patterns"""
        patterns = {
            "strategist": [
                "module_dependencies",      # Understand architecture
                "interface_boundaries",     # Find API surfaces
                "test_coverage_gaps"        # Identify risks
            ],
            "executor": [
                "implementation_details",   # Get full function bodies
                "local_dependencies",       # Find what to change
                "usage_patterns"           # Understand call sites
            ],
            "reviewer": [
                "change_impact_analysis",   # What could break
                "similar_patterns",        # Consistency checks
                "test_relationships"       # Verification paths
            ]
        }
        return patterns.get(actor_type, ["general_traversal"])
##### 4. Plugin Architecture for Extensibility The system is designed for extensibility at every level:

PluginSystem:
  # Language-specific analyzers
  analyzers:
    python:
      class: "PythonAnalyzer"
      features:
        - AST parsing via ast module
        - Type inference via mypy
        - Import resolution
        - Docstring extraction
    
    typescript:
      class: "TypeScriptAnalyzer"
      features:
        - TSC-based parsing
        - Type extraction
        - Module resolution
        - JSDoc parsing
    
    rust:
      class: "RustAnalyzer"
      features:
        - rust-analyzer integration
        - Lifetime analysis
        - Trait resolution
        - Macro expansion
    
    # Custom analyzer example
    custom_dsl:
      class: "CustomDSLAnalyzer"
      config:
        grammar: "path/to/grammar.peg"
        semantic_rules: "path/to/rules.yaml"
  
  # Index backend providers
  backends:
    graph:
      - name: "blazegraph"
        class: "BlazegraphBackend"
        scalability: "billions of triples"
        features: ["SPARQL", "reasoning", "geospatial"]
      
      - name: "neo4j"
        class: "Neo4jBackend"
        scalability: "enterprise"
        features: ["Cypher", "APOC", "GDS"]
      
      - name: "custom_graph"
        class: "MyCustomGraphDB"
        config:
          connection: "custom://localhost:7687"
    
    vector:
      - name: "faiss"
        class: "FaissBackend"
        scalability: "100M vectors"
        features: ["GPU acceleration", "HNSW"]
      
      - name: "qdrant"
        class: "QdrantBackend"
        scalability: "distributed"
        features: ["filtering", "payloads", "snapshots"]
  
  # Embedding model providers
  embedders:
    - name: "openai"
      class: "OpenAIEmbedder"
      models: ["text-embedding-3-large", "text-embedding-3-small"]
    
    - name: "local"
      class: "LocalEmbedder"
      models: ["all-MiniLM-L6-v2", "instructor-xl"]
    
    - name: "custom"
      class: "MyFineTunedEmbedder"
      model_path: "path/to/model"
##### 5. Agent Skills for Code Intelligence Agents interact with the code intelligence system through a specialized skill (`local/code-intelligence`) that contains tools for semantic search, dependency analysis, and refactoring:

# Actor references the code intelligence skill
actors:
  code_explorer:
    type: llm
    config:
      actor: anthropic/claude-3-opus
    skills:
      - local/code-intelligence   # Provides search_code_semantically,
                                  # analyze_dependencies, suggest_refactoring_targets
The `local/code-intelligence` skill uses anonymous inline tools to wrap the code intelligence subsystem. These tools are defined inline because they are tightly coupled to the skill's specific implementation and not reused elsewhere (if any were needed in multiple contexts, they would be registered independently via `agents tool add` and referenced by name):

# File: skills/code-intelligence.yaml
skill:
  name: local/code-intelligence
  description: "Semantic code search, dependency analysis, and refactoring recommendations"

  anonymous_tools:
    - name: search_code_semantically
      description: "Find code similar to a concept using vector search, enriched with dependency context"
      input_schema:
        type: object
        properties:
          query: { type: string }
          scope: { type: string, default: "all" }
          limit: { type: integer, default: 10 }
        required: [query]
      capability:
        read_only: true
      code: |
        results = ctx.code_intelligence.vector_search(
            query=params["query"],
            scope=params.get("scope", "all"),
            limit=params.get("limit", 10)
        )
        for result in results:
            result.context = ctx.code_intelligence.get_dependencies(
                entity=result.entity_id, depth=2
            )
        return results

    - name: analyze_dependencies
      description: "Analyze module dependencies using graph store"
      input_schema:
        type: object
        properties:
          module_path: { type: string }
        required: [module_path]
      capability:
        read_only: true
      code: |
        module = params["module_path"]
        deps = ctx.code_intelligence.graph_query(f'''
            PREFIX code: <https://cleveragents.ai/ontology/code#>
            SELECT ?dep ?type WHERE {{
                <{module}> code:imports* ?dep . ?dep a ?type .
            }}
        ''')
        return {
            "direct_deps": len([d for d in deps if d.distance == 1]),
            "transitive_deps": len(deps),
            "circular_deps": ctx.code_intelligence.find_circular_deps(module),
            "dependency_graph": deps
        }

    - name: suggest_refactoring_targets
      description: "Combine text, graph, and vector search to find refactoring targets"
      input_schema:
        type: object
        properties:
          scope: { type: string }
        required: [scope]
      capability:
        read_only: true
      code: |
        todos = ctx.code_intelligence.text_search(
            pattern="(TODO|FIXME|HACK):", scope=params["scope"]
        )
        complex_functions = ctx.code_intelligence.graph_query('''
            SELECT ?func ?complexity ?coverage WHERE {
                ?func a code:Function ;
                      code:hasComplexity ?complexity ;
                      code:hasTestCoverage ?coverage .
                FILTER(?complexity > 15 || ?coverage < 0.3)
            } ORDER BY DESC(?complexity)
        ''')
        smells = []
        for pattern in ["duplicate code", "long method", "large class"]:
            smells.extend(ctx.code_intelligence.find_similar_code(
                pattern=pattern, threshold=0.8
            ))
        return {
            "high_priority": complex_functions[:5],
            "technical_debt": todos,
            "code_smells": smells,
            "suggested_order": ctx.code_intelligence.rank_by_impact(
                complex_functions + todos + smells
            )
        }
##### 6. Real-time Index Synchronization The system maintains index freshness through immediate, proactive updates:

class IndexSynchronizer:
    def __init__(self):
        self.file_watcher = FileSystemWatcher()
        self.git_monitor = GitChangeMonitor()
        self.incremental_indexer = IncrementalIndexer()
    
    def on_resource_added(self, resource: Resource, project: Project):
        """When a resource is added to a project, index it immediately"""
        # Full initial indexing - happens once when resource is added
        with self.progress_reporter(f"Indexing {resource.name}") as progress:
            files = self.scan_resource(resource)
            total = len(files)
            
            # Parallel indexing for performance
            with ThreadPoolExecutor(max_workers=cpu_count()) as executor:
                futures = []
                
                for i, file in enumerate(files):
                    future = executor.submit(self.index_file_complete, file)
                    futures.append(future)
                    progress.update(i / total)
                
                # Wait for all indexing to complete
                for future in futures:
                    future.result()
        
        # Now set up watchers for incremental updates
        self.setup_watchers(resource)
        
        # Mark resource as indexed and ready
        resource.indexing_status = "ready"
        self.notify_agents_index_ready(resource)
        
        # CRITICAL: Agents can now immediately search this resource
        # No "warming up" period - indices are complete and ready
    
    def index_file_complete(self, file_path: str):
        """Comprehensive initial indexing of a file"""
        # Parse file once
        ast = self.parse_file(file_path)
        
        # Update all indices immediately
        self.update_text_index(file_path, ast)
        self.update_vector_embeddings(file_path, ast)
        self.update_graph_triples(file_path, ast)
        
        # Extract and index all metadata
        self.index_symbols(file_path, ast)
        self.index_dependencies(file_path, ast)
        self.index_complexity_metrics(file_path, ast)
    
    def setup_watchers(self, project: Project):
        # File system watching for immediate updates
        self.file_watcher.watch(
            path=project.root_path,
            events=["create", "modify", "delete"],
            callback=self.on_file_change
        )
        
        # Git monitoring for batch updates (using linked git resource)
        git_resource = project.get_linked_resource(type="git-checkout")
        self.git_monitor.watch(
            repo=git_resource,
            events=["commit", "merge", "rebase"],
            callback=self.on_git_change
        )
    
    def on_file_change(self, event: FileEvent):
        # Quick incremental update
        if event.type in ["create", "modify"]:
            # Parse changed file
            ast = self.parse_file(event.path)
            
            # Update indices
            self.update_text_index(event.path, ast)
            self.update_vector_embeddings(event.path, ast)
            self.update_graph_triples(event.path, ast)
        
        elif event.type == "delete":
            self.remove_from_indices(event.path)
    
    def on_git_change(self, event: GitEvent):
        # Batch update for git operations
        changed_files = event.get_changed_files()
        
        # Optimize batch processing
        with self.batch_updater() as updater:
            for file in changed_files:
                updater.queue_update(file)
            
            # Process in parallel
            updater.execute(parallel=True)
    
    def update_graph_triples(self, file_path: str, ast: AST):
        # Generate RDF triples from AST
        triples = []
        
        # Module-level triples
        module_uri = self.uri_for_file(file_path)
        for import_stmt in ast.imports:
            imported_uri = self.resolve_import(import_stmt)
            triples.append((module_uri, "code:imports", imported_uri))
        
        # Function-level triples
        for func in ast.functions:
            func_uri = self.uri_for_function(func)
            triples.append((module_uri, "code:contains", func_uri))
            triples.append((func_uri, "a", "code:Function"))
            triples.append((func_uri, "code:hasComplexity", func.complexity))
            
            # Call relationships
            for call in func.calls:
                called_uri = self.resolve_call(call)
                triples.append((func_uri, "code:calls", called_uri))
        
        # Update graph store
        self.graph_store.update_triples(triples)
##### 7. Fallback to Traditional Search When advanced features are unavailable, the system gracefully degrades:

class FallbackSearchProvider:
    def search(self, query: str, resources: List[Resource]) -> SearchResults:
        # Try advanced search first
        try:
            if self.vector_index.is_available():
                return self.vector_search(query, resources)
        except ServiceUnavailable:
            pass
        
        # Fallback to graph search
        try:
            if self.graph_index.is_available():
                return self.graph_search(query, resources)
        except ServiceUnavailable:
            pass
        
        # Ultimate fallback: grep-like text search
        return self.basic_text_search(query, resources)
    
    def basic_text_search(self, query: str, resources: List[Resource]):
        # Use ripgrep or similar for fast text search
        results = []
        
        for resource in resources:
            matches = ripgrep.search(
                pattern=query,
                path=resource.path,
                context_lines=3
            )
            
            for match in matches:
                results.append(SearchResult(
                    file=match.file,
                    line=match.line,
                    content=match.content,
                    score=1.0  # Basic scoring
                ))
        
        return results
#### Index Lifecycle The system follows a clear lifecycle for index management:

Index Lifecycle:
  1_resource_added:
    trigger: "agents resource add / agents project link-resource"
    action: "Immediate full indexing"
    duration: "Depends on size (10K files ~1 minute)"
    result: "All indices ready for instant search"
    
  2_code_changed:
    trigger: "File modification detected"
    action: "Immediate incremental update"
    duration: "Milliseconds per file"
    result: "Indices stay synchronized"
    
  3_resource_removed:
    trigger: "agents project unlink-resource / agents resource remove"
    action: "Immediate index cleanup"
    duration: "Seconds"
    result: "No stale data in indices"
    
  4_maintenance:
    trigger: "Scheduled or manual"
    action: "Reindex for consistency"
    duration: "Background process"
    result: "Indices optimized and verified"

Key Guarantees:
  - "No search happens on stale data"
  - "No 'index building' delays during agent execution"
  - "Changes visible in search immediately"
  - "Initial indexing is a one-time cost per resource"
#### Integration with Context Tiers The Code Intelligence system directly feeds into the three-tier context architecture:

Context Tier Integration:
  hot_tier:
    source: "Real-time results from code intelligence"
    content:
      - Currently edited files
      - Direct dependencies
      - Immediately relevant functions
    
  warm_tier:
    source: "Indexed embeddings and graph queries"
    content:
      - Recent search results
      - Cached graph traversals
      - Vector similarity matches
      - Active decision contexts
    
  cold_tier:
    source: "Historical indices and compressed data"
    content:
      - Previous plan analyses
      - Archived dependency graphs
      - Historical refactoring patterns
      - Learned codebase conventions
#### Performance Characteristics The system maintains pre-computed indices for instant search performance:

Performance Metrics:
  initial_indexing_speed:
    text_index: "10,000 files/minute"
    vector_index: "1,000 files/minute (with GPU)"
    graph_index: "5,000 files/minute"
  
  query_performance:
    text_search: "< 100ms for 1M files"
    vector_search: "< 200ms for 10M embeddings"
    graph_traversal: "< 500ms for 3-hop queries"
    
  storage_requirements:
    text_index: "~10% of source size"
    vector_index: "~1GB per 100K functions"
    graph_store: "~100MB per 10K files"
    
  scalability:
    max_files: "No hard limit (tested to 10M files)"
    max_graph_size: "1B+ triples"
    max_vectors: "100M+ embeddings"
#### Progressive Enhancement Path Organizations can adopt Code Intelligence features progressively. At each stage, existing resources are reindexed to take advantage of new capabilities:

adoption_stages:
  stage_1_basic:
    features: ["text search", "file watching"]
    requirements: ["ripgrep", "sqlite"]
    initial_setup: "Index all text content on resource add"
    benefit: "Instant exact-match search"
    
  stage_2_semantic:
    features: ["vector embeddings", "similarity search"]
    requirements: ["embedding model", "vector DB"]
    initial_setup: "Generate embeddings for all code (one-time cost)"
    benefit: "Instant semantic similarity search"
    
  stage_3_structural:
    features: ["RDF graph", "relationship queries"]
    requirements: ["graph database", "language analyzers"]
    initial_setup: "Parse and build complete knowledge graph"
    benefit: "Instant relationship queries"
    
  stage_4_intelligent:
    features: ["ML-driven ranking", "automated analysis"]
    requirements: ["GPU", "training data"]
    initial_setup: "Pre-compute ML features and rankings"
    benefit: "Instant intelligent suggestions"
    
  stage_5_custom:
    features: ["Domain-specific ontologies", "Custom analyzers"]
    requirements: ["Domain expertise", "Custom development"]
    initial_setup: "Build domain-specific indices"
    benefit: "Instant domain-aware intelligence"
This Code Intelligence & Context Discovery system ensures that CleverAgents can efficiently work with codebases of any size, providing agents with the contextual understanding they need to make intelligent decisions about code changes, refactoring, and feature development. **Summary of Timing**: - **Indexing**: Eager (happens immediately when resources are added/changed) - **Searching**: Instant (because indices are pre-computed and ready) - **Sandboxing**: Lazy (only when execution needs to modify a resource) - **Context Assembly**: Real-time (but fast because it queries ready indices) This design ensures agents never wait for index building during execution, providing a responsive and predictable experience even on massive codebases. ### Context **Note: This section describes the high-level context management system. For details on how context is discovered and indexed, see the Code Intelligence & Context Discovery section above.** Context in CleverAgents is not "dump all files into an LLM." It is a system that: * finds relevant information from resources, * injects appropriate subsets into each actor/node, * and scales to large repositories. #### Current Reality and Planned Improvements key concepts: * There is a global context concept, * But nodes only see what is injected into their prompts, * It's functional but not yet elegant, * A more advanced automated system is planned. #### Tiered Context Architecture (Hot/Warm/Cold) The system uses a sophisticated three-tier memory architecture that enables working with massive codebases without holding everything in memory: * **Hot context (hot cache)** The small set of immediately relevant chunks injected into the current actor prompt. When working on a 50,000 file codebase, hot context focuses on the immediate task (e.g., 10-20 files for a specific refactoring). * **Warm context** Recent decisions and their contexts from this plan tree - quickly accessible. Includes indexed embeddings, vector search results, and graph store representations. Maintains the decision chain that led to the current work. * **Cold storage** Historical decisions from past plans on this codebase - queryable but not in active memory. Long-term storage in SQLite or caching systems containing prior summaries, older plan artifacts, and historical patterns (e.g., "last time we refactored auth, we also had to update these services"). Promotion/demotion behavior: * System analyzes current query * Promotes relevant data upward (cold → warm → hot) * Demotes stale data out of hot to keep prompts tight * Preserves complete context snapshots for every decision This architecture leverages the key insight that software development is inherently local - even in huge codebases, individual changes typically touch a bounded set of files. The Decision Tree captures these localities. #### Actor-Specific Context Views A key missing feature identified in the notes is per-actor context views, filtering, relevance, and actor-aware context limits. The intended direction: * global context exists at plan level, * each actor gets a "view" of that context tuned to their role, * memory may be shared or per-plan depending on design choices. ##### Actor Context View Service (Proposed) A dedicated module/service that: * maintains actor-specific "context views," * tracks actor memory and relevance, * enforces actor-specific limits (tokens, file types, etc.). #### Initial Context vs Deep Context A practical approach mentioned: * Initial context is high-level (repo tree, language, overview), * Then the system searches for relevant details iteratively via RAG. This strongly suggests CleverAgents should define: * an "initial context recipe" per project type (codebase vs documents vs infra), * iterative context refinement loops during strategize/execute. ### Output Rendering Framework #### Overview CleverAgents uses a unified **Output Rendering Framework** to decouple command output data from its visual presentation. Every CLI command produces structured output through a common abstraction layer, and the active **format** determines how that output is rendered to the terminal (or piped to external consumers). The format is set via the global `--format` flag, the `format` config key, or defaults to `rich`. The framework is **reactive-first**: commands do not build a static data structure and hand it to a renderer. Instead, commands open an **output session**, create **element handles** for each piece of output (a panel, a table, a progress indicator), and write data to those handles — potentially from multiple concurrent producers. The session coordinates with a **materialization strategy** selected by the active format, which decides *when* and *how* each element's content reaches the terminal. A `rich` session renders updates in-place as they arrive; a `plain` session buffers each element and flushes sequentially; a `json` session accumulates everything and serializes once at the end. **Producer code is format-agnostic** — it writes to handles without knowing which format is active. This architecture is designed for modularity, extensibility, and future-proofing — the same session-based output can be consumed by the CLI, a future TUI, a web frontend, or programmatic integrations. The design uses a pipeline of composable stages: **session lifecycle management**, **typed element handles**, **event-driven materialization**, and **format-specific element rendering**. #### Architecture ##### Rendering Pipeline All CLI output flows through a five-stage reactive pipeline:

Command Logic ──► OutputSession ──► ElementHandles ──► MaterializationStrategy ──► Terminal/Pipe
                  (lifecycle)       (typed producers)   (format-driven policy)      (stdout/stderr)
1. **Command Logic** opens an `OutputSession` and creates typed **element handles** — `PanelHandle`, `TableHandle`, `ProgressHandle`, etc. — for each piece of output the command will produce. Handles are created in **declaration order**, which determines the canonical order in which elements appear in sequential formats. 2. **OutputSession** is the central coordinator. It owns the set of active handles, tracks their lifecycle (open → writing → closed), emits `ElementEvent` objects to the active materialization strategy, and provides a `snapshot()` method that returns a static `StructuredOutput` representing the accumulated state at any point in time. 3. **ElementHandles** are the producer-facing API. Each handle is typed for a specific element kind (panel, table, tree, etc.) and exposes write methods appropriate to that kind (`add_row()`, `set_entry()`, `set_step_status()`, etc.). Handles are **thread-safe** — multiple concurrent coroutines or threads can write to different handles simultaneously. Handles are **format-agnostic** — the producer never knows or cares which format is active. 4. **MaterializationStrategy** is a polymorphic observer selected by the active format. It receives `ElementEvent` notifications from the session and decides *when* and *how* to render content. Each strategy delegates the actual visual rendering of an element's accumulated state to a paired **ElementRenderer**. 5. **Terminal/Pipe** receives the final byte stream. The framework auto-detects whether stdout is a TTY and degrades gracefully (e.g., `rich` falls back to `table` when piped to a non-TTY unless `--format rich` was explicitly set). ##### OutputSession The `OutputSession` is the core abstraction that replaces direct construction of static output objects. Commands receive a session (typically injected by the CLI framework) and interact with it throughout their execution:

class OutputSession:
    """A live output document that coordinates element production and materialization.
    
    The session manages the lifecycle of all output elements for a single command
    invocation. It is the bridge between format-agnostic producer code and the
    format-specific materialization strategy.
    
    Thread Safety:
        The session is thread-safe. Multiple producers may create and write to
        handles concurrently. The session serializes event delivery to the
        materialization strategy using an internal event queue.
    
    Lifecycle:
        session = OutputSession.open(command, strategy)
        handle_a = session.panel("Title")       # create handles
        handle_b = session.table("Results", ...) 
        handle_a.set_entry(...)                  # write to handles (concurrent OK)
        handle_b.add_row(...)
        handle_a.close()                         # close handles when done
        handle_b.close()
        session.close()                          # finalize the session
    """
    
    # --- Session lifecycle ---
    
    command: str                                  # The command that owns this session
    session_id: str                               # Unique session identifier (ULID)
    created_at: datetime                          # Session creation timestamp
    
    _strategy: MaterializationStrategy            # The active materialization strategy
    _handles: OrderedDict[str, ElementHandle]     # handle_id → handle, in declaration order
    _event_queue: asyncio.Queue[ElementEvent]     # Internal event queue for serialization
    _state: SessionState                          # "open" | "closing" | "closed"
    _lock: threading.Lock                         # Protects handle creation/removal
    
    @classmethod
    def open(cls, command: str, strategy: MaterializationStrategy,
             metadata: dict | None = None) -> "OutputSession":
        """Open a new output session.
        
        Called by the CLI framework before command execution. The strategy is
        selected based on the resolved format (see Format Resolution).
        
        Args:
            command: The command string (e.g., "project show").
            strategy: The materialization strategy for the active format.
            metadata: Optional command metadata (user, timestamp, etc.).
        
        Returns:
            A new OutputSession ready for element creation.
        """
        ...
    
    # --- Element handle factories ---
    # Each factory creates a typed handle, registers it with the session in
    # declaration order, and emits an ElementCreated event to the strategy.
    
    def panel(self, title: str, *,
              border_style: str = "rounded",
              priority: str = "normal",
              collapse_hint: str = "auto",
              metadata: dict | None = None) -> "PanelHandle":
        """Create a panel element handle for key-value pair output."""
        ...
    
    def table(self, title: str | None, *,
              columns: list["ColumnDef"],
              summary: dict | None = None,
              max_rows_hint: int | None = None,
              sort_key: str | None = None,
              priority: str = "normal",
              collapse_hint: str = "auto",
              metadata: dict | None = None) -> "TableHandle":
        """Create a table element handle for tabular data."""
        ...
    
    def tree(self, root_label: str, *,
             root_style: str | None = None,
             max_depth_hint: int | None = None,
             show_guides: bool = True,
             priority: str = "normal",
             collapse_hint: str = "auto",
             metadata: dict | None = None) -> "TreeHandle":
        """Create a tree element handle for hierarchical data."""
        ...
    
    def progress(self, label: str, *,
                 total: int | None = None,
                 indeterminate: bool = False,
                 steps: list[str] | None = None,
                 priority: str = "normal",
                 metadata: dict | None = None) -> "ProgressHandle":
        """Create a progress indicator handle.
        
        Args:
            label: Display label for the progress indicator.
            total: Total units of work (None for indeterminate).
            indeterminate: If True, show a spinner instead of a progress bar.
            steps: Named steps to track (creates ProgressStep objects with
                   initial status "pending").
        """
        ...
    
    def status(self, message: str, *,
               level: str = "info",
               detail: str | None = None,
               priority: str = "normal",
               metadata: dict | None = None) -> "StatusHandle":
        """Create a status message handle."""
        ...
    
    def text(self, content: str = "", *,
             wrap: bool = True,
             indent: int = 0,
             priority: str = "normal",
             metadata: dict | None = None) -> "TextHandle":
        """Create a text block handle."""
        ...
    
    def code(self, content: str = "", *,
             language: str | None = None,
             line_numbers: bool = False,
             highlight_lines: list[int] | None = None,
             priority: str = "normal",
             metadata: dict | None = None) -> "CodeHandle":
        """Create a code block handle."""
        ...
    
    def diff(self, *,
             file_a: str | None = None,
             file_b: str | None = None,
             priority: str = "normal",
             metadata: dict | None = None) -> "DiffHandle":
        """Create a diff block handle."""
        ...
    
    def separator(self, style: str = "line") -> "SeparatorHandle":
        """Create a visual separator. Separators are auto-closed on creation."""
        ...
    
    def action_hint(self, commands: list[str],
                    description: str | None = None) -> "ActionHintHandle":
        """Create an action hint. Action hints are auto-closed on creation."""
        ...
    
    # --- Session operations ---
    
    def snapshot(self) -> "StructuredOutput":
        """Return a static snapshot of all elements accumulated so far.
        
        The snapshot captures the current state of every handle (open or closed)
        as a StructuredOutput object. This is used by accumulate-mode strategies
        (json/yaml) at session end, and is available at any time for logging,
        debugging, or programmatic inspection.
        """
        ...
    
    def close(self, *, exit_code: int = 0) -> "StructuredOutput":
        """Close the session and finalize all output.
        
        Any handles still open are force-closed (with a warning logged).
        Emits a SessionEnd event to the strategy. Returns the final
        StructuredOutput snapshot.
        
        Args:
            exit_code: The command's exit code (included in the snapshot).
        
        Returns:
            The final StructuredOutput snapshot.
        """
        ...
    
    # --- Context manager support ---
    
    def __enter__(self) -> "OutputSession":
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        """Auto-close session on context exit.
        
        If exiting due to an exception, sets exit_code to 1 and emits an
        error status element before closing.
        """
        ...
##### Element Handles Element handles are the producer-facing API. Each handle type wraps a specific element kind and provides methods appropriate to that kind. All handles share a common base:

class ElementHandle(Generic[E]):
    """Base class for all element handles.
    
    An element handle is a write-only view of an output element. Producers use
    handles to incrementally build element content without knowledge of the
    active format or materialization strategy.
    
    Type Parameter:
        E: The OutputElement subclass this handle wraps (e.g., Panel, Table).
    
    Thread Safety:
        Individual handle methods are thread-safe. Multiple threads may call
        methods on *different* handles concurrently. Concurrent writes to the
        *same* handle are serialized via an internal lock.
    
    Lifecycle:
        handle = session.table(...)    # Created by session factory
        handle.add_row(...)            # Write operations (zero or more)
        handle.close()                 # Finalize (required unless auto-closed)
    
    Closed Handle Behavior:
        Calling any write method on a closed handle raises ElementClosedError.
    """
    
    handle_id: str                       # Unique handle identifier (ULID)
    element_type: str                    # Semantic type ("panel", "table", etc.)
    declaration_index: int               # Position in session's declaration order
    
    _session: OutputSession              # Owning session (for event emission)
    _element: E                          # The accumulated element state
    _state: HandleState                  # "open" | "closed"
    _lock: threading.Lock                # Serializes writes to this handle
    
    def close(self) -> None:
        """Close this handle, signaling that no more data will be written.
        
        Emits an ElementClosed event to the materialization strategy.
        For buffered strategies, this triggers rendering of the element.
        """
        ...
    
    @property
    def is_open(self) -> bool:
        """Whether this handle is still accepting writes."""
        ...
    
    @property
    def element(self) -> E:
        """The accumulated element state (read-only snapshot)."""
        ...
    
    def __enter__(self) -> Self:
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        """Auto-close handle on context exit."""
        if self.is_open:
            self.close()


class PanelHandle(ElementHandle[Panel]):
    """Handle for building a Panel element incrementally.
    
    Panels are titled groups of key-value pairs. Entries can be added,
    updated, or removed after creation.
    """
    
    def set_entry(self, key: str, value: str, *,
                  style_hint: str | None = None,
                  icon: str | None = None) -> None:
        """Set or update a key-value entry in the panel.
        
        If an entry with the given key already exists, it is updated.
        Otherwise, a new entry is appended.
        
        Emits an ElementUpdated event.
        """
        ...
    
    def set_entries(self, entries: dict[str, str], *,
                    style_hints: dict[str, str] | None = None) -> None:
        """Set multiple entries at once (batch update). Emits a single event."""
        ...
    
    def remove_entry(self, key: str) -> None:
        """Remove an entry by key. Emits an ElementUpdated event."""
        ...


class TableHandle(ElementHandle[Table]):
    """Handle for building a Table element incrementally.
    
    Tables are the primary element for streamed data. Rows can be added
    one at a time or in batches as data becomes available from queries,
    API calls, or concurrent operations.
    """
    
    def add_row(self, row: dict) -> None:
        """Append a single row to the table.
        
        The row dict maps column names to cell values. Missing columns
        are filled with None. Extra columns not in the schema are ignored.
        
        Emits an ElementUpdated event (type=row_added).
        """
        ...
    
    def add_rows(self, rows: list[dict]) -> None:
        """Append multiple rows in a batch. Emits a single ElementUpdated event."""
        ...
    
    def set_summary(self, summary: dict) -> None:
        """Set or update the summary/aggregation row. Emits an ElementUpdated event."""
        ...
    
    def set_sort_key(self, column: str, *, descending: bool = False) -> None:
        """Change the sort key. Emits an ElementUpdated event."""
        ...


class TreeHandle(ElementHandle[Tree]):
    """Handle for building a Tree element incrementally.
    
    Trees are built by adding child nodes to existing nodes. The root node
    is created with the handle. Subtrees can be constructed incrementally
    as hierarchical data is discovered.
    """
    
    def add_child(self, parent_path: str | None, label: str, *,
                  style_hint: str | None = None,
                  collapsed: bool = False,
                  metadata: dict | None = None) -> str:
        """Add a child node to the tree.
        
        Args:
            parent_path: Slash-separated path to the parent node (None = root).
            label: Display label for the new node.
            style_hint: Optional color/style hint.
            collapsed: Whether this node starts collapsed in interactive renderers.
            metadata: Arbitrary data attached to the node.
        
        Returns:
            The full path to the newly created node (for use as parent_path
            in subsequent add_child calls).
        
        Emits an ElementUpdated event.
        """
        ...
    
    def set_node_style(self, path: str, style_hint: str) -> None:
        """Update the style of an existing node. Emits an ElementUpdated event."""
        ...


class ProgressHandle(ElementHandle[ProgressIndicator]):
    """Handle for updating a ProgressIndicator element.
    
    Progress handles are unique in that they are expected to receive many
    rapid updates. The materialization strategy may throttle update events
    to avoid overwhelming the terminal (e.g., limiting redraws to 10/sec).
    """
    
    def set_progress(self, current: int, total: int | None = None) -> None:
        """Update the progress counter.
        
        Args:
            current: Current progress value.
            total: Total value (can change, e.g., when total is discovered late).
        
        Emits an ElementUpdated event (may be throttled by the strategy).
        """
        ...
    
    def set_step_status(self, step_label: str, status: str) -> None:
        """Update the status of a named step.
        
        Args:
            step_label: The label of the step to update.
            status: New status — "pending" | "active" | "done" | "error" | "skipped".
        
        Emits an ElementUpdated event.
        """
        ...
    
    def set_label(self, label: str) -> None:
        """Update the progress label text. Emits an ElementUpdated event."""
        ...
    
    def increment(self, delta: int = 1) -> None:
        """Increment progress by delta. Convenience wrapper around set_progress."""
        ...


class StatusHandle(ElementHandle[StatusMessage]):
    """Handle for a status message.
    
    Status handles are typically created and immediately closed (fire-and-forget
    messages). However, they can be kept open for messages that may be revised
    (e.g., a "Working..." status that becomes "Done" or "Failed").
    """
    
    def set_message(self, message: str) -> None:
        """Update the status message text. Emits an ElementUpdated event."""
        ...
    
    def set_level(self, level: str) -> None:
        """Change the status level. Emits an ElementUpdated event."""
        ...
    
    def set_detail(self, detail: str | None) -> None:
        """Set or clear the detail text. Emits an ElementUpdated event."""
        ...


class TextHandle(ElementHandle[TextBlock]):
    """Handle for a text block. Supports appending text incrementally."""
    
    def append(self, text: str) -> None:
        """Append text to the block. Emits an ElementUpdated event."""
        ...
    
    def set_content(self, content: str) -> None:
        """Replace the entire content. Emits an ElementUpdated event."""
        ...


class CodeHandle(ElementHandle[CodeBlock]):
    """Handle for a code block."""
    
    def set_content(self, content: str) -> None:
        """Set the code content. Emits an ElementUpdated event."""
        ...
    
    def set_language(self, language: str) -> None:
        """Set the language for syntax highlighting. Emits an ElementUpdated event."""
        ...
    
    def set_highlight_lines(self, lines: list[int]) -> None:
        """Set lines to highlight. Emits an ElementUpdated event."""
        ...


class DiffHandle(ElementHandle[DiffBlock]):
    """Handle for a diff block. Hunks can be added incrementally."""
    
    def add_hunk(self, header: str, lines: list["DiffLine"]) -> None:
        """Add a diff hunk. Emits an ElementUpdated event."""
        ...
    
    def set_stats(self, insertions: int, deletions: int, **extra: int) -> None:
        """Set diff statistics. Emits an ElementUpdated event."""
        ...
##### Element Events Element handles communicate with the materialization strategy through a typed event system. Events are the sole interface between production (handles) and consumption (strategy) — this indirection is what enables format-agnostic producer code:

class ElementEvent:
    """Base class for all events emitted by element handles."""
    event_type: str                      # "created" | "updated" | "closed"
    handle_id: str                       # The handle that emitted this event
    element_type: str                    # The element kind ("panel", "table", etc.)
    timestamp: datetime                  # When the event occurred
    session_id: str                      # The owning session


class ElementCreated(ElementEvent):
    """Emitted when a new element handle is created via a session factory."""
    event_type = "created"
    declaration_index: int               # Position in session declaration order
    initial_state: OutputElement         # The element's initial state


class ElementUpdated(ElementEvent):
    """Emitted when data is written to an element handle."""
    event_type = "updated"
    update_type: str                     # Kind-specific: "entry_set", "row_added",
                                         #   "progress_changed", "step_status_changed", etc.
    delta: dict                          # The change payload (what was added/modified)
    element_snapshot: OutputElement      # The full element state after this update


class ElementClosed(ElementEvent):
    """Emitted when an element handle is closed (no more data will arrive)."""
    event_type = "closed"
    final_state: OutputElement           # The element's final accumulated state


class SessionEnd(ElementEvent):
    """Emitted when the session itself is closed."""
    event_type = "session_end"
    exit_code: int
    snapshot: "StructuredOutput"         # The complete accumulated output
##### Element Data Model (Snapshot Types) Each element handle accumulates state into a typed **snapshot object**. These are the data classes that represent a fully-built element — they are what the `ElementRenderer` receives when it is time to paint. They are also the building blocks of the `StructuredOutput` returned by `session.snapshot()`:

class OutputElement:
    """Base class for all output element snapshot types."""
    element_type: str                    # Semantic type identifier
    metadata: dict                       # Arbitrary metadata (timestamps, IDs, etc.)
    priority: str = "normal"             # "critical" | "normal" | "supplementary"
    collapse_hint: str = "auto"          # "always" | "auto" | "never" — guidance for renderers
                                         #   on whether this element can be collapsed/hidden

class Panel(OutputElement):
    """A titled group of key-value pairs."""
    element_type = "panel"
    title: str
    entries: list[PanelEntry]            # Each entry: key, value, style_hint (color, icon, etc.)
    border_style: str = "rounded"        # "rounded" | "square" | "heavy" | "none"

class PanelEntry:
    """A single key-value pair within a Panel."""
    key: str
    value: str
    style_hint: str | None = None        # Color/style for the value (e.g., "success", "warning")
    icon: str | None = None              # Optional icon/prefix character

class Table(OutputElement):
    """A tabular data set with typed columns."""
    element_type = "table"
    title: str | None
    columns: list[ColumnDef]             # name, type, alignment, width_hint, sortable
    rows: list[dict]                     # Column name → cell value
    summary: dict | None                 # Optional aggregation row (totals, counts)
    max_rows_hint: int | None            # Suggest truncation for large datasets
    sort_key: str | None                 # Default sort column

class ColumnDef:
    """Schema for a single table column."""
    name: str                            # Column display name
    type: str = "string"                 # "string" | "number" | "boolean" | "datetime" | "id"
    alignment: str = "left"              # "left" | "right" | "center"
    width_hint: int | None = None        # Suggested character width (None = auto)
    sortable: bool = False               # Whether this column can be sorted
    style_hint: str | None = None        # Default style for cells in this column

class Tree(OutputElement):
    """A hierarchical tree structure."""
    element_type = "tree"
    root: TreeNode                       # Recursive node structure
    max_depth_hint: int | None           # Suggest depth truncation
    show_guides: bool = True             # Whether to show tree guide lines

class TreeNode:
    """A node in a tree structure."""
    label: str
    style_hint: str | None               # Color/style for this node
    children: list["TreeNode"]
    collapsed: bool = False              # Hint: start collapsed in interactive renderers
    metadata: dict                       # Arbitrary data attached to the node

class StatusMessage(OutputElement):
    """A status line (success, warning, error, info)."""
    element_type = "status"
    level: str                           # "ok" | "warn" | "error" | "info"
    message: str
    detail: str | None                   # Optional detail text

class ProgressIndicator(OutputElement):
    """A progress bar or spinner for long-running operations."""
    element_type = "progress"
    label: str
    current: int | None
    total: int | None
    indeterminate: bool = False          # Spinner mode vs. progress bar mode
    steps: list[ProgressStep] | None     # Named steps with status (pending/active/done)

class ProgressStep:
    """A named step within a progress indicator."""
    label: str
    status: str                          # "pending" | "active" | "done" | "error" | "skipped"

class CodeBlock(OutputElement):
    """A block of source code with optional syntax highlighting."""
    element_type = "code"
    content: str
    language: str | None                 # For syntax highlighting
    line_numbers: bool = False
    highlight_lines: list[int] | None    # Lines to emphasize

class DiffBlock(OutputElement):
    """A unified diff display."""
    element_type = "diff"
    hunks: list[DiffHunk]
    file_a: str | None
    file_b: str | None
    stats: dict | None                   # insertions, deletions, etc.

class DiffHunk:
    """A single hunk within a diff."""
    header: str                          # @@ line range @@
    lines: list[DiffLine]

class DiffLine:
    """A single line in a diff hunk."""
    type: str                            # "context" | "add" | "remove"
    content: str
    line_number_old: int | None
    line_number_new: int | None

class TextBlock(OutputElement):
    """A free-form text block (descriptions, rationale, etc.)."""
    element_type = "text"
    content: str
    wrap: bool = True
    indent: int = 0

class Separator(OutputElement):
    """A visual separator between logical groups."""
    element_type = "separator"
    style: str = "line"                  # "line" | "blank" | "double"

class ActionHint(OutputElement):
    """A suggested next-step action for the user."""
    element_type = "action_hint"
    commands: list[str]                  # Suggested CLI commands
    description: str | None

class StructuredOutput:
    """Static snapshot of a complete command output.
    
    This is the accumulated state of all elements at a point in time.
    It is produced by OutputSession.snapshot() and OutputSession.close().
    
    Uses:
    - Final serialization for json/yaml formats
    - Logging and audit trails
    - Programmatic inspection and testing
    - TUI widget data binding (initial state)
    """
    command: str                         # The command that produced this output
    session_id: str                      # The session that produced this output
    elements: list[OutputElement]        # Ordered list of element snapshots
    exit_code: int = 0
    timing: dict | None                  # start_time, end_time, duration
    metadata: dict                       # command-specific metadata
##### Materialization Strategies The materialization strategy is the format-side counterpart to the output session. It receives element events and decides *when* and *how* to render content. Each strategy is paired with an `ElementRenderer` that handles the actual visual formatting of individual elements. The strategy pattern creates a clean separation between **timing/ordering policy** (when to render) and **visual formatting** (how to render). This means the same `PlainElementRenderer` can be used whether elements arrive all-at-once or are streamed concurrently — the strategy handles the coordination.

class MaterializationStrategy(Protocol):
    """Interface for format-driven output materialization.
    
    A materialization strategy receives element lifecycle events from the
    OutputSession and decides when to render element content to the output
    stream. Strategies do not render elements themselves — they delegate
    to a paired ElementRenderer at the appropriate time.
    
    The strategy is the mechanism by which format-agnostic producer code
    produces correct output regardless of format. The producer writes to
    handles; the strategy decides what reaches the terminal and when.
    """
    
    strategy_name: str                   # "live" | "sequential_buffer" | "accumulate"
    
    def bind(self, renderer: "ElementRenderer",
             terminal_caps: "TerminalCapabilities") -> None:
        """Bind this strategy to a renderer and terminal capabilities.
        
        Called once during format resolution, before the session opens.
        The strategy retains a reference to the renderer for use during
        event handling. The output stream is provided separately via
        on_session_begin, since the session owns the stream.
        """
        ...
    
    def on_session_begin(self, session: OutputSession, stream: IO) -> None:
        """Called when the session opens. The strategy receives the output
        stream and may write preamble (e.g., opening JSON bracket)."""
        ...
    
    def on_element_created(self, event: ElementCreated) -> None:
        """Called when a new element handle is created."""
        ...
    
    def on_element_updated(self, event: ElementUpdated) -> None:
        """Called when data is written to an element handle."""
        ...
    
    def on_element_closed(self, event: ElementClosed) -> None:
        """Called when an element handle is closed."""
        ...
    
    def on_session_end(self, event: SessionEnd) -> None:
        """Called when the session closes. The strategy may write epilogue."""
        ...


class LiveMaterializer(MaterializationStrategy):
    """Materialization strategy for the `rich` format.
    
    Renders element updates in real-time using terminal cursor movement.
    Multiple elements can be visually active and updating simultaneously.
    The terminal display is a live document that is rewritten in place.
    
    Behavior:
    - on_element_created: Allocates screen region for the element, renders
      initial (possibly empty) visual state.
    - on_element_updated: Re-renders the element in place using cursor
      movement. For progress indicators, updates may be throttled to a
      maximum refresh rate (default: 15 fps) to avoid terminal flooding.
    - on_element_closed: Renders the final state and freezes the screen
      region (no further updates). May apply a visual transition (e.g.,
      spinner resolves to a checkmark).
    - on_session_end: Finalizes the display, moves cursor to end, and
      restores normal terminal scrolling.
    
    Screen Layout:
    Elements are arranged vertically in declaration order. Each element
    occupies a contiguous block of terminal lines. The materializer tracks
    the line offset and height of each element's region. When an element's
    height changes (e.g., a table gains rows), subsequent elements are
    shifted down.
    
    Concurrent Updates:
    Updates from multiple handles are coalesced into a single frame refresh
    at the target frame rate. The materializer maintains a dirty-element set
    and redraws all dirty elements in a single pass per frame.
    """
    strategy_name = "live"
    
    _frame_rate: float = 15.0            # Maximum redraws per second
    _element_regions: OrderedDict[str, ScreenRegion]  # handle_id → screen region
    _dirty_set: set[str]                 # handle_ids that need redraw
    _frame_timer: asyncio.TimerHandle    # Coalescing timer for frame redraws


class SequentialBufferMaterializer(MaterializationStrategy):
    """Materialization strategy for `plain`, `color`, and `table` formats.
    
    Buffers element content and renders elements sequentially in declaration
    order. An element's content is rendered to the output stream only when
    its handle is closed. If handles are closed out of declaration order,
    the out-of-order element's rendered content is held in a buffer until
    all preceding elements have been rendered.
    
    This strategy ensures that static, scrolling output formats produce
    coherent sequential output even when producers write to handles
    concurrently and close them in arbitrary order.
    
    Behavior:
    - on_element_created: Records the element's declaration index. No output.
    - on_element_updated: Buffers the update internally. No output.
    - on_element_closed: If this element is the next in declaration order,
      renders it immediately (and any buffered subsequent elements that are
      also closed). Otherwise, buffers the rendered content.
    - on_session_end: Force-renders any remaining buffered elements (handles
      that were never closed, in declaration order).
    
    Example (two tables populated concurrently):
    1. Handle A (index 0) created — table "Resources"
    2. Handle B (index 1) created — table "Validations"
    3. Handle B receives rows, Handle A receives rows (interleaved)
    4. Handle B closes (index 1) — rendered content buffered (waiting for A)
    5. Handle A closes (index 0) — A is rendered to stream, then buffered B
       is rendered to stream
    
    Result: Output shows table A followed by table B, regardless of the
    order in which data arrived or handles closed.
    """
    strategy_name = "sequential_buffer"
    
    _next_render_index: int = 0          # The declaration index to render next
    _rendered_buffers: dict[int, str]     # index → pre-rendered content (waiting)
    _closed_set: set[int]                # Declaration indices of closed elements


class AccumulateMaterializer(MaterializationStrategy):
    """Materialization strategy for `json` and `yaml` formats.
    
    Accumulates all element data silently until the session ends, then
    serializes the complete StructuredOutput as a single JSON or YAML
    document.
    
    Behavior:
    - on_element_created: No output.
    - on_element_updated: No output.
    - on_element_closed: No output.
    - on_session_end: Calls session.snapshot() to get the final
      StructuredOutput, then delegates to the ElementRenderer's
      serialize() method for complete document serialization.
    
    This strategy is the simplest — it ignores all intermediate events and
    only acts on session_end. It exists as a distinct strategy (rather than
    a special case) to maintain the uniform strategy interface.
    """
    strategy_name = "accumulate"
##### ElementRenderer Protocol While the `MaterializationStrategy` controls *when* elements are rendered, the `ElementRenderer` controls *how* each element type is visually formatted. Each format has a paired `ElementRenderer` implementation:

class ElementRenderer(Protocol):
    """Interface for format-specific element rendering.
    
    An ElementRenderer knows how to paint each element type for a specific
    output format. It is called by the MaterializationStrategy when it is
    time to render an element.
    
    Implementations:
    - PlainElementRenderer: ASCII text, no escapes
    - ColorElementRenderer: ANSI-colored text, same layout as plain
    - TableElementRenderer: Unicode box-drawing with color
    - RichElementRenderer: Advanced terminal features (cursor, animation)
    - JsonElementRenderer: JSON serialization
    - YamlElementRenderer: YAML serialization
    """
    
    format_name: str                     # "plain", "color", "table", "rich", "json", "yaml"
    
    def render_panel(self, panel: Panel, stream: IO) -> None:
        """Render a panel element to the stream."""
        ...
    
    def render_table(self, table: Table, stream: IO) -> None:
        """Render a table element to the stream."""
        ...
    
    def render_tree(self, tree: Tree, stream: IO) -> None:
        """Render a tree element to the stream."""
        ...
    
    def render_status(self, status: StatusMessage, stream: IO) -> None:
        """Render a status message to the stream."""
        ...
    
    def render_progress(self, progress: ProgressIndicator, stream: IO) -> None:
        """Render a progress indicator to the stream."""
        ...
    
    def render_code(self, code: CodeBlock, stream: IO) -> None:
        """Render a code block to the stream."""
        ...
    
    def render_diff(self, diff: DiffBlock, stream: IO) -> None:
        """Render a diff block to the stream."""
        ...
    
    def render_text(self, text: TextBlock, stream: IO) -> None:
        """Render a text block to the stream."""
        ...
    
    def render_separator(self, separator: Separator, stream: IO) -> None:
        """Render a visual separator to the stream."""
        ...
    
    def render_action_hint(self, hint: ActionHint, stream: IO) -> None:
        """Render an action hint to the stream."""
        ...
    
    def render_element(self, element: OutputElement, stream: IO) -> None:
        """Dispatch to the appropriate render method based on element type.
        
        This is the primary entry point used by materialization strategies.
        It uses a dispatch table to route to the correct typed method.
        """
        dispatch = {
            "panel": self.render_panel,
            "table": self.render_table,
            "tree": self.render_tree,
            "status": self.render_status,
            "progress": self.render_progress,
            "code": self.render_code,
            "diff": self.render_diff,
            "text": self.render_text,
            "separator": self.render_separator,
            "action_hint": self.render_action_hint,
        }
        handler = dispatch.get(element.element_type)
        if handler:
            handler(element, stream)
    
    def serialize(self, output: StructuredOutput, stream: IO) -> None:
        """Serialize a complete StructuredOutput to the stream.
        
        Used by AccumulateMaterializer for json/yaml formats. For visual
        formats (plain/color/table/rich), this method iterates over
        output.elements and calls render_element for each.
        """
        ...
    
    def can_render(self, terminal_caps: "TerminalCapabilities") -> bool:
        """Whether this renderer can operate in the given terminal environment."""
        ...
##### Format Resolution The active format is resolved using a precedence chain: 1. **CLI flag**: `--format ` on the command line (highest priority). 2. **Environment variable**: `CLEVERAGENTS_FORMAT=`. 3. **Config file**: The `format` key in the global config (`agents config set format `). 4. **TTY detection**: If stdout is not a TTY and no explicit format was set, fall back to `plain` (not `rich`), since non-TTY consumers cannot interpret ANSI codes or cursor movement. 5. **Default**: `rich`. Once the format is resolved, the CLI framework selects the corresponding `(MaterializationStrategy, ElementRenderer)` pair from the `RendererRegistry`, opens an `OutputSession` bound to that strategy, and passes the session to the command implementation. #### Format Specifications ##### `plain` — Plain Text **Philosophy**: Maximum portability. Output is pure ASCII text with no escape codes, no box-drawing characters, and no color. Suitable for piping to files, logs, grep, awk, or any non-terminal consumer. **Rendering rules**: - **Panels**: Rendered as indented key-value pairs with a header line. - **Tables**: Rendered as aligned columns separated by whitespace (no box drawing). Column headers are separated from data by a dashed line. - **Trees**: Rendered with ASCII indentation using `+--` and `|` characters. - **Status messages**: Prefixed with `[OK]`, `[WARN]`, `[ERROR]`, `[INFO]`. - **Progress**: Rendered as static status lines (no animation). Steps shown as `[x]` (done), `[ ]` (pending), `[>]` (active). - **Diffs**: Standard unified diff format. - **Code blocks**: Raw text with optional line numbers. - **No ANSI escape codes** of any kind. - **No Unicode characters** beyond basic ASCII (no box drawing, no checkmarks, no arrows). **Example** (`agents --format plain project show local/api-service`):

$ agents --format plain project show local/api-service

Project Details
  Name: local/api-service
  ID: 01HXM4T08Y0N5R9VZ4QX4BPTZ1
  Description: Backend API
  Resources: 2
  Remote: no
  Created: 2026-02-08 12:46

Linked Resources
  Resource          Type            Sandbox              Read-Only
  ----------------  --------------  --------------------  ---------
  local/api-repo    git-checkout    git_worktree          no
  local/staging-db  local/database  transaction_rollback  yes

Validations (3)
  val_01HXM5A  pytest --cov=src --cov-fail-under=80  required
  val_01HXM5B  ruff check .                          required
  val_01HXM5C  node scripts/check-bundle-size.js     info

Context
  Include: repo
  Exclude: **/node_modules/**
  Max File Size: 1 MB

Indexing Status
  Text Index: ready
  Vector Index: ready
  Graph Store: disabled
  Indexed Files: 347
  Last Indexed: 12:48

Active Plans
  Plan ID   Action               Phase
  --------  -------------------  -------
  01HXM7A9  local/code-coverage  execute

[OK] Project loaded
**Example** (`agents --format plain plan list`):

$ agents --format plain plan list --phase execute

Plans
  ID        Phase    State       Action               Project            Elapsed
  --------  -------  ----------  -------------------  -----------------  ---------
  01HXM7A9  execute  processing  local/code-coverage  local/api-service  00:01:12

Filters
  Phase: execute
  State: (any)
  Project: (any)
  Action: (any)

Summary
  Total: 1
  Processing: 1
  Completed: 0
  Errored: 0

[OK] 1 plan listed
##### `color` — Colored Plain Text **Philosophy**: Same structural layout as `plain`, but with ANSI color codes applied to improve readability. No box-drawing characters, no cursor movement, no animation. **Rendering rules**: - **Identical layout to `plain`**, but with color applied: - **Headers/titles**: Bold cyan. - **Keys**: Bold blue. - **Values**: Default color, with semantic coloring: - Success/positive: Green. - Warnings/attention: Yellow. - Errors/failures: Red. - Identifiers/names: Cyan. - Counts/numbers: Default (white). - **Table headers**: Bold cyan with underlines rendered as dim dashes. - **Status prefixes**: `[OK]` in green, `[WARN]` in yellow, `[ERROR]` in red, `[INFO]` in blue. - **Diff lines**: `+` lines green, `-` lines red, `@@` headers cyan. - **No box-drawing characters** — uses the same whitespace/dash layout as `plain`. - **No cursor movement or animation** — pure scrolling output. - **Respects `NO_COLOR` environment variable**: If `NO_COLOR` is set, `color` format falls back to `plain`. **Example** (`agents --format color project show local/api-service`):

$ agents --format color project show local/api-service

Project Details
  Name: local/api-service
  ID: 01HXM4T08Y0N5R9VZ4QX4BPTZ1
  Description: Backend API
  Resources: 2
  Remote: no
  Created: 2026-02-08 12:46

Linked Resources
  Resource          Type            Sandbox              Read-Only
  ----------------  --------------  --------------------  ---------
  local/api-repo    git-checkout    git_worktree          no
  local/staging-db  local/database  transaction_rollback  yes

Validations (3)
  val_01HXM5A  pytest --cov=src --cov-fail-under=80  required
  val_01HXM5B  ruff check .                          required
  val_01HXM5C  node scripts/check-bundle-size.js     info

Context
  Include: repo
  Exclude: **/node_modules/**
  Max File Size: 1 MB

Indexing Status
  Text Index: ready
  Vector Index: ready
  Graph Store: disabled
  Indexed Files: 347
  Last Indexed: 12:48

Active Plans
  Plan ID   Action               Phase
  --------  -------------------  -------
  01HXM7A9  local/code-coverage  execute

[OK] Project loaded
##### `table` — ASCII Box-Drawing Tables **Philosophy**: Structured, visually distinct panels and tables using Unicode box-drawing characters (╭╮╰╯│─). Uses color. This is the style shown in the existing CLI examples throughout this document. **Rendering rules**: - **Panels**: Rendered as bordered boxes with title in the top border. Uses `╭─╮│╰─╯` characters for rounded corners. - **Tables**: Rendered inside bordered boxes with column-aligned headers and separator lines using `─` and `│`. - **Trees**: Rendered inside a bordered box using `├──`, `└──`, `│` tree guide characters. - **Status messages**: Use Unicode indicators: `✓` (green) for OK, `⚠` (yellow) for WARN, `✗` (red) for ERROR, `ℹ` (blue) for INFO. - **Progress**: Rendered as a step list inside a panel with `✓`, `⏳`, `•` markers. - **Color scheme**: Same semantic coloring as `color` format, applied within box structures. - **No animation or cursor movement** — the boxes are static, scrolling output. **Distinction from `rich`**: The `table` format uses the same box-drawing panels and color as `rich` for static content, but it does **not** use any dynamic or interactive terminal features. There are no animated spinners, no live-updating progress bars, no cursor movement, and no in-place redraws. All output is static and scrolls sequentially. Where `rich` would show a spinning `⠋` and a live progress bar, `table` renders a static snapshot using fixed markers (`✓`, `⏳`, `•`). This makes `table` suitable for terminals without advanced capabilities, and for output that will be reviewed after the fact (e.g., scrollback buffers). **Example** (`agents --format table project show local/api-service`):

$ agents --format table project show local/api-service

╭─ Project Details ──────────────╮
│ Name: local/api-service        │
│ ID: 01HXM4T08Y0N5R9VZ4QX4BPTZ1 │
│ Description: Backend API       │
│ Resources: 2                   │
│ Remote: no                     │
│ Created: 2026-02-08 12:46      │
╰────────────────────────────────╯

╭─ Linked Resources ──────────────────────────────────────────────────────╮
│ Resource          Type            Sandbox              Read-Only        │
│ ────────────────  ──────────────  ────────────────────  ─────────       │
│ local/api-repo   git-checkout    git_worktree         no                │
│ local/staging-db local/database  transaction_rollback yes               │
╰─────────────────────────────────────────────────────────────────────────╯

╭─ Validations (3) ───────────────────────────────────────────╮
│ val_01HXM5A  pytest --cov=src --cov-fail-under=80  required │
│ val_01HXM5B  ruff check .                          required │
│ val_01HXM5C  node scripts/check-bundle-size.js     info     │
╰─────────────────────────────────────────────────────────────╯

╭─ Context ───────────────────╮
│ Include: repo               │
│ Exclude: **/node_modules/** │
│ Max File Size: 1 MB         │
╰─────────────────────────────╯

╭─ Indexing Status ──────────╮
│ Text Index: ready          │
│ Vector Index: ready        │
│ Graph Store: disabled      │
│ Indexed Files: 347         │
│ Last Indexed: 12:48        │
╰────────────────────────────╯

╭─ Active Plans ──────────────────────────╮
│ Plan ID   Action               Phase    │
│ ────────  ───────────────────  ───────  │
│ 01HXM7A9  local/code-coverage  execute  │
╰─────────────────────────────────────────╯

✓ OK Project loaded
**Example** (`agents --format table plan execute 01HXM8C2ZK`): Unlike `rich` mode which would show animated spinners and a live progress bar, the `table` format renders a static snapshot of execution state:

$ agents --format table plan execute 01HXM8C2ZK

╭─ Execution ──────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Phase: execute                   │
│ Sandbox: git_worktree            │
│ Worker: local/executor           │
│ Started: 12:58:10                │
│ Attempt: 1                       │
╰──────────────────────────────────╯

╭─ Strategy Summary ─────────────────────╮
│ Decisions: 8                           │
│ Invariants: 2                          │
│ Planned Child Plans: 2+                │
│ Estimated Files: ~12                   │
│ Risk: low                              │
╰────────────────────────────────────────╯

╭─ Progress ────────╮
│  Collect context │
│  Run tools       │
│  Build changeset │
│  Validate        │
╰───────────────────╯

✓ OK Execution started
##### `rich` — Modern Rich CLI Elements **Philosophy**: The premium interactive terminal experience. Uses advanced terminal capabilities: cursor movement, inline updates, animated spinners, live-updating progress bars, collapsible sections, syntax highlighting, and dynamic layout. This is the default format. **Rendering rules**: - **Panels**: Rich bordered panels with rounded corners, title bars, and optional collapse/expand behavior. Panels may animate into view. - **Tables**: Full-featured tables with automatic column sizing, truncation with ellipsis, sortable column indicators, alternating row shading, and horizontal scrolling for wide tables. - **Trees**: Interactive collapsible trees. Nodes expand/collapse with visual animation. Color-coded by node type. Depth guides use dotted lines. - **Status messages**: Use animated checkmarks/spinners that resolve to final state. Success messages may briefly flash or highlight. - **Progress**: Live-updating progress bars with: - Animated spinners (Braille, dots, or bars depending on terminal capability). - Elapsed time and ETA. - Per-step status with animated transitions (pending → active → done). - Multi-line progress for parallel operations. - **Diffs**: Syntax-highlighted side-by-side or unified diffs with line numbers, change highlighting at the character level (not just line level), and navigable hunks. - **Code blocks**: Full syntax highlighting using terminal colors (256-color or truecolor when available). Line numbers in dim color. Highlighted lines with background color. - **Dynamic layout**: Adapts to terminal width. Narrow terminals get a stacked layout; wide terminals get side-by-side panels. - **Live updates**: Long-running commands (plan execute, plan status) use live-updating displays that redraw in place rather than scrolling. - **Graceful degradation**: If the terminal does not support required capabilities (e.g., no truecolor, no cursor movement), the renderer automatically falls back to `table` rendering for those elements. **Example** (`agents --format rich plan execute 01HXM8C2ZK`): The `rich` format produces output that cannot be fully represented in static documentation — animated spinners cycle in place, progress bars fill smoothly, and elements update without scrolling. The rendering below is a static snapshot of what the terminal would display at a given moment:

$ agents --format rich plan execute 01HXM8C2ZK

╭─ Execution ──────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Phase: execute                   │
│ Sandbox: git_worktree            │
│ Worker: local/executor           │
│ Started: 12:58:10                │
╰──────────────────────────────────╯

 Collecting context...                      (⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ animates in place)
  ├── repo: local/api-repo 
  └── db: local/staging-db 

╭─ Strategy Summary ──────────────────────────────────────────────────────╮
│ 8 decisions │ 2 invariants │ 2+ child plans │ ~12 files │ risk: low     │
╰─────────────────────────────────────────────────────────────────────────╯

Progress ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  42%  elapsed 0:01:12  ETA 0:01:40
   Collect context .................. 0.8s
   Run tools (8 calls) ............. 12.4s
   Build changeset ................. (running)     (animates in place)
   Validate ........................ (pending)

╭─ Live Tool Calls ───────────────────────────────────────╮
│ #6 read_file   src/auth/__init__.py     0.1s           │
│ #7 write_file  tests/test_auth.py       0.2s           │
│ #8 edit_file   src/auth/session.py      ...            │
╰─────────────────────────────────────────────────────────╯
In `rich` mode: - The Braille spinner characters (`⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏`) animate in real-time, cycling in place without scrolling. - The progress bar (`━━━`) fills smoothly as work completes. - Completed steps appear with a green `✓` via in-place line update (the line rewrites, it does not scroll). - The "Live Tool Calls" panel scrolls its content internally, showing only the most recent N calls. - The terminal is not flooded with scrolling text — elements update in place using cursor movement. **Example** (`agents --format rich version`):

$ agents --format rich version

╭─────────────────────────────────────╮
│  CleverAgents CLI  v1.0.0           │
│  channel: stable                    │
╰─────────────────────────────────────╯

╭─ Build ─────────────────────────────╮
│ Build Date: 2026-02-08              │
│ Commit: a17c3f9                     │
│ Schema: v3                          │
│ Platform: linux-x86_64              │
│ Python: 3.13.1                      │
╰─────────────────────────────────────╯

╭─ Dependencies ─────────────────────────────────────────────────────────╮
│ LangGraph 0.2.60 │ LangChain 0.3.18 │ MCP SDK 1.4.0 │ Pydantic 2.10.4  │
╰────────────────────────────────────────────────────────────────────────╯

✓ OK Version reported
In `rich` mode, the version card may use background colors, bold gradients, or subtle box shadows (depending on terminal truecolor support). Elements may animate into view with a brief slide or fade transition. ##### `json` — JSON Data Structure **Philosophy**: Machine-readable output for programmatic consumption. Every command produces a well-defined JSON object. No ANSI color codes in the structural data. Color codes may appear only **within** text values that represent verbatim content (such as code blocks or diff output where the original text contained ANSI sequences), but all structural keys, labels, and metadata are plain strings. **Rendering rules**: - **Top-level structure**: Always a JSON object with a standard envelope:

  {
    "command": "project show",
    "status": "ok",
    "exit_code": 0,
    "data": { ... },
    "timing": { "duration_ms": 42 },
    "metadata": { ... }
  }
  
- **Panels**: Rendered as nested objects within `data`. - **Tables**: Rendered as arrays of objects within `data`. - **Trees**: Rendered as nested objects with `children` arrays. - **Status messages**: Included in a `messages` array within the envelope. - **Progress**: Not rendered (JSON output is non-interactive; progress is omitted). - **Diffs**: Rendered as structured objects with `hunks` arrays. - **No ANSI codes** in any structural element. Raw ANSI codes are preserved only in string values that represent verbatim terminal output. - **Pretty-printed** by default (indented). Compact mode available via `--json-compact` (future option). - **Consistent schema per command**: Each command's JSON schema is stable and documented, enabling reliable programmatic parsing. **Example** (`agents --format json project show local/api-service`):

{
  "command": "project show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "project": {
      "name": "local/api-service",
      "id": "01HXM4T08Y0N5R9VZ4QX4BPTZ1",
      "description": "Backend API",
      "type": "local",
      "remote": false,
      "created_at": "2026-02-08T12:46:00Z"
    },
    "linked_resources": [
      {
        "name": "local/api-repo",
        "type": "git-checkout",
        "sandbox_strategy": "git_worktree",
        "read_only": false
      },
      {
        "name": "local/staging-db",
        "type": "local/database",
        "sandbox_strategy": "transaction_rollback",
        "read_only": true
      }
    ],
    "validations": [
      {
        "id": "val_01HXM5A",
        "command": "pytest --cov=src --cov-fail-under=80",
        "mode": "required",
        "timeout": 600,
        "resource": "repo"
      },
      {
        "id": "val_01HXM5B",
        "command": "ruff check .",
        "mode": "required",
        "timeout": 300,
        "resource": null
      },
      {
        "id": "val_01HXM5C",
        "command": "node scripts/check-bundle-size.js",
        "mode": "informational",
        "timeout": 300,
        "resource": null
      }
    ],
    "context": {
      "include_resources": ["repo"],
      "exclude_paths": ["**/node_modules/**"],
      "max_file_size_bytes": 1048576
    },
    "indexing": {
      "text_index": "ready",
      "vector_index": "ready",
      "graph_store": "disabled",
      "indexed_files": 347,
      "last_indexed_at": "2026-02-08T12:48:00Z"
    },
    "active_plans": [
      {
        "plan_id": "01HXM7A9",
        "action": "local/code-coverage",
        "phase": "execute"
      }
    ]
  },
  "timing": {
    "duration_ms": 42
  },
  "messages": [
    { "level": "ok", "text": "Project loaded" }
  ]
}
**Example** (`agents --format json plan list --phase execute`):

{
  "command": "plan list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plans": [
      {
        "id": "01HXM7A9",
        "phase": "execute",
        "state": "processing",
        "action": "local/code-coverage",
        "project": "local/api-service",
        "elapsed": "00:01:12"
      }
    ],
    "filters": {
      "phase": "execute",
      "state": null,
      "project": null,
      "action": null
    },
    "summary": {
      "total": 1,
      "processing": 1,
      "completed": 0,
      "errored": 0
    }
  },
  "timing": {
    "duration_ms": 18
  },
  "messages": [
    { "level": "ok", "text": "1 plan listed" }
  ]
}
##### `yaml` — YAML Data Structure **Philosophy**: Same data as `json` but in YAML format. Preferred by users who find YAML more readable for configuration and scripting workflows. Follows the same structural conventions as `json`. **Rendering rules**: - **Same data envelope** as JSON (`command`, `status`, `exit_code`, `data`, `timing`, `messages`). - **YAML 1.2 compliant** output. - **Multi-line strings** use YAML block scalars (`|` for literal, `>` for folded) when appropriate. - **No ANSI codes** in structural elements (same rule as JSON). - **Sorted keys** for deterministic output. **Example** (`agents --format yaml project show local/api-service`):

command: project show
status: ok
exit_code: 0
data:
  project:
    name: local/api-service
    id: 01HXM4T08Y0N5R9VZ4QX4BPTZ1
    description: Backend API
    type: local
    remote: false
    created_at: "2026-02-08T12:46:00Z"
  linked_resources:
    - name: local/api-repo
      type: git-checkout
      sandbox_strategy: git_worktree
      read_only: false
    - name: local/staging-db
      type: local/database
      sandbox_strategy: transaction_rollback
      read_only: true
  validations:
    - id: val_01HXM5A
      command: "pytest --cov=src --cov-fail-under=80"
      mode: required
      timeout: 600
      resource: repo
    - id: val_01HXM5B
      command: "ruff check ."
      mode: required
      timeout: 300
      resource: null
    - id: val_01HXM5C
      command: "node scripts/check-bundle-size.js"
      mode: informational
      timeout: 300
      resource: null
  context:
    include_resources:
      - repo
    exclude_paths:
      - "**/node_modules/**"
    max_file_size_bytes: 1048576
  indexing:
    text_index: ready
    vector_index: ready
    graph_store: disabled
    indexed_files: 347
    last_indexed_at: "2026-02-08T12:48:00Z"
  active_plans:
    - plan_id: 01HXM7A9
      action: local/code-coverage
      phase: execute
timing:
  duration_ms: 42
messages:
  - level: ok
    text: Project loaded
#### Format Comparison Matrix | Capability | `plain` | `color` | `table` | `rich` | `json` | `yaml` | |---|---|---|---|---|---|---| | Color codes | No | Yes | Yes | Yes | No | No | | Box drawing | No | No | Yes | Yes | No | No | | Animation/spinners | No | No | No | Yes | No | No | | Live updates | No | No | No | Yes | No | No | | Cursor movement | No | No | No | Yes | No | No | | Syntax highlighting | No | No | No | Yes | No | No | | Collapsible sections | No | No | No | Yes | No | No | | Machine-parseable | Partially | Partially | No | No | Yes | Yes | | Pipe-safe | Yes | No* | No | No | Yes | Yes | | Unicode required | No | No | Yes | Yes | No | No | | TTY required | No | No | No | Yes** | No | No | \* `color` output can be piped if the consumer understands ANSI codes (e.g., `less -R`). \** `rich` gracefully degrades to `table` when stdout is not a TTY. #### Renderer Registration and Extension The framework uses a **registry pattern** for format renderers, enabling third-party or plugin renderers. Each format is registered as a `(MaterializationStrategy, ElementRenderer)` pair — the strategy controls timing/ordering, and the renderer controls visual formatting:

@dataclass
class FormatRegistration:
    """A registered format: its strategy factory, renderer factory, and fallback."""
    strategy_factory: Callable[[TerminalCapabilities], MaterializationStrategy]
    renderer_factory: Callable[[TerminalCapabilities], ElementRenderer]
    fallback: str | None                 # Format name to fall back to, or None


class RendererRegistry:
    """Central registry for format (strategy, renderer) pairs.
    
    Formats are registered by name and resolved at runtime based on the
    active format and terminal capabilities. The registry supports dynamic
    registration, enabling plugins to add custom formats (e.g., 'html',
    'csv', 'markdown').
    
    Built-in registrations:
        "rich"  → (LiveMaterializer, RichElementRenderer), fallback="table"
        "table" → (SequentialBufferMaterializer, TableElementRenderer), fallback="color"
        "color" → (SequentialBufferMaterializer, ColorElementRenderer), fallback="plain"
        "plain" → (SequentialBufferMaterializer, PlainElementRenderer), fallback=None
        "json"  → (AccumulateMaterializer, JsonElementRenderer), fallback=None
        "yaml"  → (AccumulateMaterializer, YamlElementRenderer), fallback=None
    """
    
    _formats: dict[str, FormatRegistration] = {}
    
    @classmethod
    def register(cls, format_name: str,
                 strategy_factory: Callable[[TerminalCapabilities], MaterializationStrategy],
                 renderer_factory: Callable[[TerminalCapabilities], ElementRenderer],
                 fallback: str | None = None) -> None:
        """Register a format.
        
        Args:
            format_name: The format identifier (e.g., 'rich', 'json').
            strategy_factory: Callable that creates a MaterializationStrategy,
                given terminal capabilities.
            renderer_factory: Callable that creates an ElementRenderer,
                given terminal capabilities.
            fallback: Optional fallback format name if this format cannot
                operate in the current terminal environment.
        """
        cls._formats[format_name] = FormatRegistration(
            strategy_factory=strategy_factory,
            renderer_factory=renderer_factory,
            fallback=fallback,
        )
    
    @classmethod
    def resolve(cls, format_name: str,
                terminal_caps: TerminalCapabilities
                ) -> tuple[MaterializationStrategy, ElementRenderer]:
        """Resolve the best (strategy, renderer) pair for the given format.
        
        Walks the fallback chain if the requested format's renderer cannot
        operate in the current terminal environment. Returns the first pair
        where the renderer reports can_render(terminal_caps) == True.
        
        Raises:
            ValueError: If no usable format is found (should never happen
                since 'plain' has no fallback and always works).
        """
        current = format_name
        visited: set[str] = set()
        
        while current and current not in visited:
            visited.add(current)
            registration = cls._formats.get(current)
            if registration is None:
                break
            
            renderer = registration.renderer_factory(terminal_caps)
            if renderer.can_render(terminal_caps):
                strategy = registration.strategy_factory(terminal_caps)
                strategy.bind(renderer, terminal_caps=terminal_caps)
                return strategy, renderer
            
            current = registration.fallback
        
        # Ultimate fallback is always plain
        plain = cls._formats["plain"]
        renderer = plain.renderer_factory(terminal_caps)
        strategy = plain.strategy_factory(terminal_caps)
        strategy.bind(renderer, terminal_caps=terminal_caps)
        return strategy, renderer
    
    @classmethod
    def available_formats(cls) -> list[str]:
        """Return all registered format names."""
        return sorted(cls._formats.keys())
    
    @classmethod
    def is_registered(cls, format_name: str) -> bool:
        """Check if a format is registered."""
        return format_name in cls._formats
##### Terminal Capability Detection The framework detects terminal capabilities to guide format resolution, strategy selection, and renderer fallback:

@dataclass
class TerminalCapabilities:
    """Detected capabilities of the output terminal.
    
    This dataclass is populated once at CLI startup and passed to the
    RendererRegistry for format resolution. It is also available to
    individual strategies and renderers for fine-grained adaptation
    (e.g., adjusting column widths to terminal width, choosing between
    256-color and truecolor palettes).
    """
    is_tty: bool                         # Is stdout a TTY?
    width: int                           # Terminal width in columns
    height: int                          # Terminal height in rows
    supports_ansi: bool                  # Supports basic ANSI escape codes?
    supports_256_color: bool             # Supports 256-color palette?
    supports_truecolor: bool             # Supports 24-bit truecolor?
    supports_unicode: bool               # Supports Unicode (box-drawing, etc.)?
    supports_cursor_movement: bool       # Supports cursor repositioning?
    supports_alternate_screen: bool      # Supports alternate screen buffer?
    no_color: bool                       # Is NO_COLOR environment variable set?
    term_program: str | None             # TERM_PROGRAM value (e.g., "iTerm2", "vscode")
    
    @classmethod
    def detect(cls) -> "TerminalCapabilities":
        """Auto-detect terminal capabilities from the environment.
        
        Detection logic:
        - is_tty: os.isatty(sys.stdout.fileno())
        - width/height: os.get_terminal_size() with fallback to (80, 24)
        - supports_ansi: True if is_tty and not Windows legacy console
        - supports_256_color: True if TERM contains "256color" or COLORTERM is set
        - supports_truecolor: True if COLORTERM is "truecolor" or "24bit"
        - supports_unicode: True if locale encoding is UTF-8
        - supports_cursor_movement: True if is_tty and TERM is not "dumb"
        - supports_alternate_screen: True if supports_cursor_movement
        - no_color: True if NO_COLOR environment variable is set (any value)
        - term_program: Value of TERM_PROGRAM environment variable
        """
        ...
##### Plugin Format Registration Third-party plugins can register custom formats using the registry:

# Example: Registering a custom 'csv' format plugin
from cleveragents.output import RendererRegistry, SequentialBufferMaterializer

class CsvElementRenderer(ElementRenderer):
    """Renders tables as CSV, other elements as plain text."""
    format_name = "csv"
    
    def render_table(self, table: Table, stream: IO) -> None:
        writer = csv.writer(stream)
        writer.writerow([col.name for col in table.columns])
        for row in table.rows:
            writer.writerow([row.get(col.name, "") for col in table.columns])
    
    def can_render(self, terminal_caps: TerminalCapabilities) -> bool:
        return True  # CSV works everywhere

# Register at plugin load time
RendererRegistry.register(
    format_name="csv",
    strategy_factory=lambda caps: SequentialBufferMaterializer(),
    renderer_factory=lambda caps: CsvElementRenderer(),
    fallback="plain",
)
#### Edge Cases and Special Behaviors ##### Error Output Errors are rendered through the same framework. When a command raises an exception or signals an error, the session's context manager (`__exit__`) catches the exception, creates a `StatusMessage` with `level="error"` and an optional `TextBlock` with details, and closes the session with `exit_code=1`. In `json`/`yaml` formats, errors produce:

{
  "command": "project show",
  "status": "error",
  "exit_code": 1,
  "error": {
    "code": "NOT_FOUND",
    "message": "Project 'local/nonexistent' not found",
    "detail": "No project with name 'local/nonexistent' exists. Run 'agents project list' to see available projects.",
    "suggestions": [
      "agents project list",
      "agents project create local/nonexistent"
    ]
  },
  "timing": { "duration_ms": 5 }
}
##### Empty Results When a list command returns no results, all formats handle it gracefully: - **plain/color**: Prints a message like `No projects found.` - **table**: Renders an empty table with headers and a `(empty)` message. - **rich**: Renders a dimmed panel with an empty-state message and suggested actions. - **json/yaml**: Returns an empty array in the appropriate data field. ##### Large Data Sets For commands that may return large result sets (e.g., `resource list` with thousands of resources): - **plain/color/table** (SequentialBufferMaterializer): The table handle accumulates rows as the producer adds them. Since the buffer is in-memory, very large result sets benefit from the `max_rows_hint` — the renderer truncates display at N rows with a `(... and N more rows)` indicator. The full data is still available in the snapshot for json/yaml. - **rich** (LiveMaterializer): Uses a virtual-scrolling table that renders only visible rows. Shows a count indicator (e.g., "Showing 1-50 of 2,847"). New rows animate into view as they are added. - **json/yaml** (AccumulateMaterializer): Accumulates and emits a complete array at session end. Streaming JSON lines (one JSON object per row) for very large sets is a future consideration. ##### Nested/Recursive Structures Tree-like data (resource trees, decision trees, plan hierarchies) may be arbitrarily deep. Renderers respect `max_depth_hint`: - **plain/color**: Truncate at depth N with a `... (N more levels)` indicator. - **table**: Same truncation, rendered inside a box. - **rich**: Collapsible tree nodes — deep levels start collapsed. User can expand interactively if the terminal supports it. - **json/yaml**: Full depth — no truncation (programmatic consumers need complete data). ##### Mixed Content Some commands produce mixed output (e.g., `plan status` has panels, tables, progress bars, and status messages). Each element is created via its own handle on the session, and the materialization strategy renders them in declaration order. The `ElementRenderer` is responsible for visual spacing and grouping between heterogeneous elements (e.g., inserting blank lines between a panel and a table in `plain` format, or adding visual margins in `rich` format). ##### Producer Error Mid-Stream When a producer encounters an error while writing to a handle (e.g., an API call fails while populating a table), the framework handles it as follows: 1. **The handle is closed with partial data** — the producer catches its exception, optionally calls `handle.close()` (or lets the context manager close it), and then creates a `StatusMessage` handle with `level="error"` to report the failure. 2. **The materialization strategy renders whatever was accumulated** — a table with 3 of an expected 10 rows is rendered with those 3 rows, followed by the error message. This is better than rendering nothing. 3. **The session's exit code is set to 1** — indicating partial failure. 4. **For `json`/`yaml` formats**, the accumulated snapshot includes both the partial data and the error message in the `messages` array, giving programmatic consumers full visibility. Example of producer error handling:

async def list_resources(session: OutputSession, client: ApiClient) -> None:
    table = session.table("Resources", columns=[
        ColumnDef(name="Name"), ColumnDef(name="Type"), ColumnDef(name="Status"),
    ])
    
    try:
        async for resource in client.list_resources():
            table.add_row({
                "Name": resource.name,
                "Type": resource.type,
                "Status": resource.status,
            })
    except ApiError as e:
        table.close()  # Close with partial data
        session.status(f"Error fetching resources: {e}", level="error")
        return
    
    table.close()
    session.status(f"{table.element.row_count} resources listed", level="ok")
##### Abandoned Handles If a handle is never explicitly closed and the session ends (either normally via `session.close()` or via the context manager's `__exit__`), the session force-closes all remaining open handles: 1. A warning is logged (not rendered to the user): `"Handle {handle_id} ({element_type}) was not explicitly closed; force-closing at session end."` 2. The handle is closed with its current accumulated state. 3. The materialization strategy processes the `ElementClosed` event normally. This ensures that no data is silently lost, even if producer code forgets to close a handle due to an unhandled code path. ##### Back-Pressure and Throttling The `LiveMaterializer` (used by `rich` format) limits terminal redraws to its configured frame rate (default 15 fps). When handles emit updates faster than the frame rate: 1. Updates are coalesced — the materializer tracks a **dirty set** of handle IDs that have been updated since the last frame. 2. On each frame tick, all dirty elements are redrawn in a single pass, and the dirty set is cleared. 3. The event queue between session and strategy uses bounded capacity. If the queue fills (producer is vastly faster than rendering), the session's `_emit_event` method drops `ElementUpdated` events for handles that are already in the dirty set (since the next frame will redraw them anyway). `ElementCreated` and `ElementClosed` events are never dropped. For `SequentialBufferMaterializer` and `AccumulateMaterializer`, there is no back-pressure concern — updates are buffered in memory and never rendered incrementally. ##### Cancellation Semantics When a command is cancelled (e.g., user presses Ctrl+C): 1. The session receives a cancellation signal and enters the `"closing"` state. 2. All open handles are force-closed with their current state. 3. A `StatusMessage` with `level="warn"` and message `"Operation cancelled"` is emitted. 4. The session closes with `exit_code=130` (standard SIGINT exit code). 5. **For `rich` format**: The `LiveMaterializer` freezes the display, resolves any active spinners to a cancellation indicator (e.g., yellow `⚠`), and moves the cursor to the end of the output. 6. **For buffered formats**: Any elements that have been rendered stay on screen. Pending buffered elements are flushed in order, followed by the cancellation message. 7. **For `json`/`yaml`**: The accumulated snapshot is serialized with `"status": "cancelled"` and the appropriate exit code. ##### Interleaved Status Messages During Concurrent Production When multiple producers are running concurrently (e.g., populating two tables), status messages may be created at any time by any producer. The materialization strategy handles these based on format: - **`rich`** (LiveMaterializer): Status messages are rendered immediately in a dedicated status region at the bottom of the display (below all element regions). Multiple concurrent status messages stack vertically. - **`plain`/`color`/`table`** (SequentialBufferMaterializer): Status messages created during concurrent production are treated as elements in declaration order, just like tables and panels. A status message created between two table creations will be rendered between those tables. Status messages created *after* all tables will render after all tables are flushed. - **`json`/`yaml`** (AccumulateMaterializer): All status messages are collected in the `messages` array of the final snapshot, ordered by timestamp. #### Integration with Future TUI The reactive `OutputSession` architecture is intentionally designed to serve as the data layer for a future TUI (text user interface). The session's event-driven model maps directly to TUI widget patterns: 1. **Element handles become observable data sources.** A TUI `MaterializationStrategy` (e.g., `TuiMaterializer`) would subscribe to element events and route them to TUI widgets. The producer code (command logic) is completely unaware of whether it is driving a CLI, TUI, or web frontend — it writes to handles identically in all cases. 2. **Element types map to TUI widgets:** - `PanelHandle` → info pane or detail card widget - `TableHandle` → sortable, filterable data grid widget (rows arrive incrementally via `add_row` events) - `TreeHandle` → collapsible tree view widget (nodes arrive incrementally via `add_child` events) - `ProgressHandle` → animated progress bar or step-list widget - `StatusHandle` → toast notification or status bar message - `CodeHandle` → syntax-highlighted code viewer widget - `DiffHandle` → side-by-side diff viewer widget 3. **Interactive features are additive.** The TUI can offer features that the CLI cannot — sorting table columns, filtering rows, collapsing/expanding tree nodes, searching within code blocks — without any changes to producer code. These features are implemented in the TUI's `ElementRenderer` and widget layer. 4. **Concurrent updates are native.** Because the session already supports multiple concurrent producers writing to different handles, the TUI naturally displays multiple simultaneously-updating widgets (e.g., two tables being populated in parallel by concurrent operations). The `TuiMaterializer` routes events to widgets, and each widget redraws independently using the TUI framework's event loop. 5. **The `StructuredOutput` snapshot** provides the initial state when navigating to a completed session in the TUI (e.g., reviewing a past command's output), while live sessions use the event stream. The separation between production (element handles), timing (materialization strategy), and presentation (element renderer) ensures that the same command logic supports CLI, TUI, and web frontends without modification — only the `(MaterializationStrategy, ElementRenderer)` pair changes. #### Programmatic Usage Examples This section demonstrates how command implementations use the Output Rendering Framework through the `OutputSession` API, and how the same producer code produces correct output across all formats. ##### Example 1: Simple Static Command Output The simplest usage — a command that creates elements, populates them synchronously, and closes them. No concurrency, no streaming. **Producer code** (`agents project show`):

async def cmd_project_show(session: OutputSession, project_name: str) -> None:
    """Implementation of 'agents project show <project>'."""
    project = await api.get_project(project_name)
    resources = await api.list_project_resources(project.id)
    validations = await api.list_project_validations(project.id)
    
    # --- Build output elements ---
    
    # Panel: Project details
    with session.panel("Project Details") as panel:
        panel.set_entries({
            "Name": project.name,
            "ID": project.id,
            "Description": project.description,
            "Resources": str(len(resources)),
            "Remote": "yes" if project.remote else "no",
            "Created": project.created_at.strftime("%Y-%m-%d %H:%M"),
        }, style_hints={
            "Name": "identifier",
            "Resources": "number",
            "Remote": "success" if not project.remote else "info",
            "Created": "success",
        })
    
    # Table: Linked resources
    with session.table("Linked Resources", columns=[
        ColumnDef(name="Resource", type="string", style_hint="identifier"),
        ColumnDef(name="Type"),
        ColumnDef(name="Sandbox"),
        ColumnDef(name="Read-Only"),
    ]) as table:
        for r in resources:
            table.add_row({
                "Resource": r.name,
                "Type": r.type,
                "Sandbox": r.sandbox_strategy,
                "Read-Only": "yes" if r.read_only else "no",
            })
    
    # Table: Validations
    with session.table(f"Validations ({len(validations)})", columns=[
        ColumnDef(name="ID", type="id", style_hint="identifier"),
        ColumnDef(name="Command"),
        ColumnDef(name="Mode"),
    ]) as table:
        for v in validations:
            table.add_row({
                "ID": v.id,
                "Command": v.command,
                "Mode": v.mode,
            })
    
    # Status: Final message
    session.status("Project loaded", level="ok")
**What this produces in `plain` format:**

Project Details
  Name: local/api-service
  ID: 01HXM4T08Y0N5R9VZ4QX4BPTZ1
  Description: Backend API
  Resources: 2
  Remote: no
  Created: 2026-02-08 12:46

Linked Resources
  Resource          Type            Sandbox              Read-Only
  ----------------  --------------  --------------------  ---------
  local/api-repo    git-checkout    git_worktree          no
  local/staging-db  local/database  transaction_rollback  yes

Validations (3)
  ID           Command                                Mode
  -----------  -------------------------------------  -----------
  val_01HXM5A  pytest --cov=src --cov-fail-under=80   required
  val_01HXM5B  ruff check .                           required
  val_01HXM5C  node scripts/check-bundle-size.js      informational

[OK] Project loaded
**What this produces in `rich` format:**

╭─ Project Details ──────────────╮
│ Name: local/api-service        │
│ ID: 01HXM4T08Y0N5R9VZ4QX4BPTZ1 │
│ Description: Backend API       │
│ Resources: 2                   │
│ Remote: no                     │
│ Created: 2026-02-08 12:46      │
╰────────────────────────────────╯

╭─ Linked Resources ──────────────────────────────────────────────────────╮
│ Resource          Type            Sandbox              Read-Only        │
│ ────────────────  ──────────────  ────────────────────  ─────────       │
│ local/api-repo   git-checkout    git_worktree         no                │
│ local/staging-db local/database  transaction_rollback yes               │
╰─────────────────────────────────────────────────────────────────────────╯

╭─ Validations (3) ───────────────────────────────────────────────────╮
│ ID           Command                                Mode            │
│ ───────────  ─────────────────────────────────────  ───────────     │
│ val_01HXM5A  pytest --cov=src --cov-fail-under=80   required        │
│ val_01HXM5B  ruff check .                           required        │
│ val_01HXM5C  node scripts/check-bundle-size.js      informational   │
╰─────────────────────────────────────────────────────────────────────╯

✓ OK Project loaded
**What this produces in `json` format:**

{
  "command": "project show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "project_details": {
      "Name": "local/api-service",
      "ID": "01HXM4T08Y0N5R9VZ4QX4BPTZ1",
      "Description": "Backend API",
      "Resources": "2",
      "Remote": "no",
      "Created": "2026-02-08 12:46"
    },
    "linked_resources": [
      {
        "Resource": "local/api-repo",
        "Type": "git-checkout",
        "Sandbox": "git_worktree",
        "Read-Only": "no"
      },
      {
        "Resource": "local/staging-db",
        "Type": "local/database",
        "Sandbox": "transaction_rollback",
        "Read-Only": "yes"
      }
    ],
    "validations": [
      {
        "ID": "val_01HXM5A",
        "Command": "pytest --cov=src --cov-fail-under=80",
        "Mode": "required"
      },
      {
        "ID": "val_01HXM5B",
        "Command": "ruff check .",
        "Mode": "required"
      },
      {
        "ID": "val_01HXM5C",
        "Command": "node scripts/check-bundle-size.js",
        "Mode": "informational"
      }
    ]
  },
  "timing": { "duration_ms": 42 },
  "messages": [
    { "level": "ok", "text": "Project loaded" }
  ]
}
In all three formats, the producer code is **identical**. The `OutputSession` and its materialization strategy handle the differences transparently. ##### Example 2: Streaming Rows into a Table A command that streams rows into a table as results arrive from a paginated API. The table handle stays open while the producer fetches pages. **Producer code** (`agents resource list`):

async def cmd_resource_list(session: OutputSession, project: str | None) -> None:
    """Implementation of 'agents resource list'."""
    
    # Create the table handle — it will accumulate rows as we stream them
    table = session.table("Resources", columns=[
        ColumnDef(name="Name", type="string", style_hint="identifier"),
        ColumnDef(name="Type"),
        ColumnDef(name="Project"),
        ColumnDef(name="Sandbox"),
        ColumnDef(name="Status"),
    ], sort_key="Name")
    
    # Create a progress indicator for the fetch operation
    progress = session.progress("Fetching resources...", indeterminate=True)
    
    # Stream pages from the API
    count = 0
    async for page in api.list_resources_paginated(project=project):
        for resource in page.items:
            table.add_row({
                "Name": resource.name,
                "Type": resource.type,
                "Project": resource.project,
                "Sandbox": resource.sandbox_strategy,
                "Status": resource.status,
            })
            count += 1
        
        # Update progress label with count so far
        progress.set_label(f"Fetching resources... ({count} found)")
    
    # Close the progress indicator (it has served its purpose)
    progress.close()
    
    # Set summary and close the table
    table.set_summary({"total": count})
    table.close()
    
    # Final status
    session.status(f"{count} resources listed", level="ok")
**What this looks like in `plain` format** (SequentialBufferMaterializer): The progress indicator is rendered as a static line. The table is buffered until `table.close()` is called, then rendered in full. The user sees nothing until the fetch is complete — then the entire result appears at once:

Fetching resources... (47 found) [done]

Resources
  Name                  Type            Project            Sandbox              Status
  --------------------  --------------  -----------------  --------------------  --------
  local/api-repo        git-checkout    local/api-service  git_worktree          active
  local/staging-db      local/database  local/api-service  transaction_rollback  active
  local/docs-repo       git-checkout    local/docs-site    git_worktree          active
  ... (44 more rows)

  Total: 47

[OK] 47 resources listed
**What this looks like in `rich` format** (LiveMaterializer): The progress spinner animates in real-time. The table updates in-place as rows arrive — each new row appears at the bottom of the table, the row count updates, and the terminal display is rewritten without scrolling. This is a static snapshot of the live display mid-fetch:

 Fetching resources... (23 found)

╭─ Resources ────────────────────────────────────────────────────────────────────────────╮
│ Name                  Type            Project            Sandbox              Status   │
│ ────────────────────  ──────────────  ─────────────────  ────────────────────  ──────  │
│ local/api-repo        git-checkout    local/api-service  git_worktree          active  │
│ local/staging-db      local/database  local/api-service  transaction_rollback  active  │
│ local/docs-repo       git-checkout    local/docs-site    git_worktree          active  │
│   ...                                                                                  │
│ local/test-fixtures   git-checkout    local/api-service  git_worktree          active  │
│                                                                                        │
│ Showing 1-23 of 23 (fetching...)                                                       │
╰────────────────────────────────────────────────────────────────────────────────────────╯
In `rich` mode, the spinner animates, the table grows as rows arrive, and the row count updates — all in-place without scrolling. When the fetch completes, the spinner resolves to `✓`, and the table shows its final state. ##### Example 3: Concurrent Parallel Operations (Two Tables Simultaneously) This is the key motivating example for the reactive architecture. Two tables are populated simultaneously by parallel workers, and the producer code is completely format-agnostic. **Producer code** (`agents plan status` — showing resources and active tool calls concurrently):

async def cmd_plan_status(session: OutputSession, plan_id: str) -> None:
    """Implementation of 'agents plan status <plan_id>'.
    
    This command fetches plan metadata, then concurrently streams two data
    sources: resource statuses and active tool call logs. Both data sources
    are long-running — they produce results over several seconds as the
    backend resolves each item.
    """
    plan = await api.get_plan(plan_id)
    
    # Panel: Plan metadata (created and closed synchronously)
    with session.panel("Plan") as panel:
        panel.set_entries({
            "Plan ID": plan.id,
            "Phase": plan.phase,
            "State": plan.state,
            "Action": plan.action,
            "Project": plan.project,
            "Started": plan.started_at.strftime("%H:%M:%S"),
        }, style_hints={
            "Plan ID": "identifier",
            "Phase": "info",
            "State": "warning" if plan.state == "processing" else "success",
        })
    
    # Create both table handles BEFORE starting concurrent producers.
    # Declaration order determines rendering order in sequential formats.
    resource_table = session.table("Resource Status", columns=[
        ColumnDef(name="Resource", style_hint="identifier"),
        ColumnDef(name="Type"),
        ColumnDef(name="Status"),
        ColumnDef(name="Latency", type="string", alignment="right"),
    ])
    
    tool_table = session.table("Tool Call Log", columns=[
        ColumnDef(name="#", type="number", alignment="right"),
        ColumnDef(name="Tool"),
        ColumnDef(name="Target"),
        ColumnDef(name="Result"),
        ColumnDef(name="Duration", type="string", alignment="right"),
    ])
    
    # --- Run two producers concurrently ---
    # Each producer writes to its own handle. Neither producer knows
    # which format is active. The materialization strategy handles
    # the coordination.
    
    async def stream_resources():
        """Producer A: streams resource status checks."""
        async for status in api.stream_resource_statuses(plan.id):
            resource_table.add_row({
                "Resource": status.resource_name,
                "Type": status.resource_type,
                "Status": status.status,
                "Latency": f"{status.latency_ms}ms",
            })
        resource_table.close()
    
    async def stream_tool_calls():
        """Producer B: streams tool call results."""
        async for call in api.stream_tool_calls(plan.id):
            tool_table.add_row({
                "#": call.sequence_number,
                "Tool": call.tool_name,
                "Target": call.target,
                "Result": call.result_summary,
                "Duration": f"{call.duration_ms}ms",
            })
        tool_table.close()
    
    # Launch both producers concurrently
    await asyncio.gather(stream_resources(), stream_tool_calls())
    
    # Final status
    session.status(f"Plan {plan_id} status retrieved", level="ok")
**What this produces in `plain` format** (SequentialBufferMaterializer): Both tables are populated concurrently, but the materializer buffers each one and renders them in **declaration order** when their handles close. The user sees nothing until the first-declared table (Resource Status) closes, then it prints. Then when the second table (Tool Call Log) closes, it prints. Data may have arrived interleaved across both tables, but the output is perfectly sequential:

Plan
  Plan ID: 01HXM7A9
  Phase: execute
  State: processing
  Action: local/code-coverage
  Project: local/api-service
  Started: 12:58:10

Resource Status
  Resource          Type            Status   Latency
  ----------------  --------------  -------  -------
  local/api-repo    git-checkout    ready    42ms
  local/staging-db  local/database  ready    128ms

Tool Call Log
  #   Tool        Target                  Result         Duration
  --  ----------  ----------------------  -------------  --------
   1  read_file   src/auth/__init__.py    200 lines      0.1s
   2  read_file   src/auth/session.py     340 lines      0.1s
   3  write_file  tests/test_auth.py      created        0.2s
   4  edit_file   src/auth/session.py     12 lines +/-   0.3s
   5  run_tests   pytest tests/test_auth  3 passed       2.1s

[OK] Plan 01HXM7A9 status retrieved
**What this produces in `rich` format** (LiveMaterializer): Both tables are visible simultaneously and update in-place as data arrives. This snapshot shows the display mid-stream — the resource table has two rows and the tool call table has three so far:

╭─ Plan ──────────────────────────────╮
│ Plan ID: 01HXM7A9                   │
│ Phase: execute                      │
│ State: processing                   │
│ Action: local/code-coverage         │
│ Project: local/api-service          │
│ Started: 12:58:10                   │
╰─────────────────────────────────────╯

╭─ Resource Status  ───────────────────────────────────────────╮
│ Resource          Type            Status   Latency            │
│ ────────────────  ──────────────  ───────  ───────            │
│ local/api-repo    git-checkout    ready    42ms               │
│ local/staging-db  local/database  ready    128ms              │
│                                                               │
│ 2 resources (streaming...)                                    │
╰───────────────────────────────────────────────────────────────╯

╭─ Tool Call Log  ────────────────────────────────────────────────────╮
│ #   Tool        Target                  Result         Duration      │
│ ──  ──────────  ──────────────────────  ─────────────  ────────      │
│  1  read_file   src/auth/__init__.py    200 lines      0.1s          │
│  2  read_file   src/auth/session.py     340 lines      0.1s          │
│  3  write_file  tests/test_auth.py      created        0.2s          │
│                                                                      │
│ 3 calls (streaming...)                                               │
╰──────────────────────────────────────────────────────────────────────╯
In `rich` mode, both tables have animated spinners in their titles indicating active streaming. As new rows arrive from either producer, the corresponding table's display is updated in-place. When a producer finishes and closes its handle, the spinner resolves to a `✓` and the "(streaming...)" indicator is removed. The other table continues updating independently. **What this produces in `json` format** (AccumulateMaterializer): Nothing is printed until the session closes. Then the complete accumulated state is serialized:

{
  "command": "plan status",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan": {
      "Plan ID": "01HXM7A9",
      "Phase": "execute",
      "State": "processing",
      "Action": "local/code-coverage",
      "Project": "local/api-service",
      "Started": "12:58:10"
    },
    "resource_status": [
      { "Resource": "local/api-repo", "Type": "git-checkout", "Status": "ready", "Latency": "42ms" },
      { "Resource": "local/staging-db", "Type": "local/database", "Status": "ready", "Latency": "128ms" }
    ],
    "tool_call_log": [
      { "#": 1, "Tool": "read_file", "Target": "src/auth/__init__.py", "Result": "200 lines", "Duration": "0.1s" },
      { "#": 2, "Tool": "read_file", "Target": "src/auth/session.py", "Result": "340 lines", "Duration": "0.1s" },
      { "#": 3, "Tool": "write_file", "Target": "tests/test_auth.py", "Result": "created", "Duration": "0.2s" },
      { "#": 4, "Tool": "edit_file", "Target": "src/auth/session.py", "Result": "12 lines +/-", "Duration": "0.3s" },
      { "#": 5, "Tool": "run_tests", "Target": "pytest tests/test_auth", "Result": "3 passed", "Duration": "2.1s" }
    ]
  },
  "timing": { "duration_ms": 3200 },
  "messages": [
    { "level": "ok", "text": "Plan 01HXM7A9 status retrieved" }
  ]
}
The critical point: **the producer code in all three formats is exactly the same**. The `asyncio.gather` call runs both producers concurrently regardless of format. The materialization strategy — `LiveMaterializer`, `SequentialBufferMaterializer`, or `AccumulateMaterializer` — transparently decides how that concurrent data reaches the user. ##### Example 4: Progress with Concurrent Sub-Operations A command that executes a multi-step process with a progress indicator, where some steps involve parallel sub-operations. **Producer code** (`agents plan execute`):

async def cmd_plan_execute(session: OutputSession, plan_id: str) -> None:
    """Implementation of 'agents plan execute <plan_id>'."""
    plan = await api.get_plan(plan_id)
    
    # Panel: Execution metadata
    with session.panel("Execution") as panel:
        panel.set_entries({
            "Plan": plan.id,
            "Phase": "execute",
            "Sandbox": plan.sandbox_strategy,
            "Worker": plan.worker,
            "Started": datetime.now().strftime("%H:%M:%S"),
        })
    
    # Progress indicator with named steps
    progress = session.progress("Executing plan", total=4, steps=[
        "Collect context",
        "Run tools",
        "Build changeset",
        "Validate",
    ])
    
    # Step 1: Collect context
    progress.set_step_status("Collect context", "active")
    context = await api.collect_context(plan.id)
    progress.set_step_status("Collect context", "done")
    progress.set_progress(1, 4)
    
    # Step 2: Run tools (parallel sub-operations)
    progress.set_step_status("Run tools", "active")
    tool_results = await api.run_tools(plan.id, context)
    progress.set_step_status("Run tools", "done")
    progress.set_progress(2, 4)
    
    # Step 3: Build changeset
    progress.set_step_status("Build changeset", "active")
    changeset = await api.build_changeset(plan.id, tool_results)
    progress.set_step_status("Build changeset", "done")
    progress.set_progress(3, 4)
    
    # Step 4: Validate
    progress.set_step_status("Validate", "active")
    validation = await api.validate_changeset(plan.id, changeset)
    progress.set_step_status("Validate", "done")
    progress.set_progress(4, 4)
    
    progress.close()
    
    # Summary panel
    with session.panel("Strategy Summary") as panel:
        panel.set_entries({
            "Decisions": str(changeset.decision_count),
            "Invariants": str(changeset.invariant_count),
            "Planned Child Plans": f"{changeset.child_plan_count}+",
            "Estimated Files": f"~{changeset.file_count}",
            "Risk": changeset.risk_level,
        })
    
    # Final status
    if validation.passed:
        session.status("Execution complete — all validations passed", level="ok")
    else:
        session.status(
            f"Execution complete — {validation.failure_count} validation(s) failed",
            level="warn",
            detail=validation.summary,
        )
**What this looks like in `plain` format:** The progress indicator renders as a static step list. Since `SequentialBufferMaterializer` buffers each element until its handle closes, the progress indicator is not visible during execution — it appears as a completed snapshot after the fact:

Execution
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Phase: execute
  Sandbox: git_worktree
  Worker: local/executor
  Started: 12:58:10

Executing plan  [4/4]
  [x] Collect context
  [x] Run tools
  [x] Build changeset
  [x] Validate

Strategy Summary
  Decisions: 8
  Invariants: 2
  Planned Child Plans: 2+
  Estimated Files: ~12
  Risk: low

[OK] Execution complete — all validations passed
**What this looks like in `rich` format:** The progress indicator is live — the spinner animates, the progress bar fills, and steps transition from pending to active to done in real-time. This snapshot shows the display mid-execution (step 3 active):

╭─ Execution ──────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Phase: execute                   │
│ Sandbox: git_worktree            │
│ Worker: local/executor           │
│ Started: 12:58:10                │
╰──────────────────────────────────╯

Executing plan ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  50%  elapsed 0:00:13
   Collect context .................. 0.8s
   Run tools ...................... 12.4s
   Build changeset ................. (running)
   Validate ........................ (pending)
When execution completes, the progress indicator resolves to its final state (all steps `✓`), the Strategy Summary panel appears below it, and the final status message is displayed. ##### Example 5: Error Mid-Stream with Partial Output A command where one of multiple concurrent producers fails, demonstrating graceful partial output. **Producer code** (hypothetical `agents resource verify`):

async def cmd_resource_verify(session: OutputSession, project: str) -> None:
    """Verify all resources in a project. Some verifications may fail."""
    resources = await api.list_project_resources(project)
    
    # Create a table that will be populated concurrently
    results_table = session.table("Verification Results", columns=[
        ColumnDef(name="Resource", style_hint="identifier"),
        ColumnDef(name="Type"),
        ColumnDef(name="Check"),
        ColumnDef(name="Status"),
        ColumnDef(name="Detail"),
    ])
    
    # Progress indicator
    progress = session.progress(
        "Verifying resources",
        total=len(resources),
        steps=[r.name for r in resources],
    )
    
    # Verify each resource concurrently
    async def verify_one(resource):
        progress.set_step_status(resource.name, "active")
        try:
            result = await api.verify_resource(resource.id)
            results_table.add_row({
                "Resource": resource.name,
                "Type": resource.type,
                "Check": result.check_name,
                "Status": "pass" if result.passed else "fail",
                "Detail": result.detail,
            })
            progress.set_step_status(
                resource.name,
                "done" if result.passed else "error",
            )
        except ApiError as e:
            results_table.add_row({
                "Resource": resource.name,
                "Type": resource.type,
                "Check": "connection",
                "Status": "error",
                "Detail": str(e),
            })
            progress.set_step_status(resource.name, "error")
        progress.increment()
    
    # Launch all verifications concurrently
    await asyncio.gather(
        *[verify_one(r) for r in resources],
        return_exceptions=True,  # Don't fail fast — collect all results
    )
    
    progress.close()
    results_table.close()
    
    # Summarize
    snapshot = results_table.element
    pass_count = sum(1 for r in snapshot.rows if r["Status"] == "pass")
    fail_count = sum(1 for r in snapshot.rows if r["Status"] in ("fail", "error"))
    
    if fail_count == 0:
        session.status(f"All {pass_count} resources verified", level="ok")
    else:
        session.status(
            f"{fail_count} of {pass_count + fail_count} resources failed verification",
            level="error",
        )
**What this produces in `plain` format** (after all concurrent verifications complete):

Verifying resources  [3/3]
  [x] local/api-repo
  [!] local/staging-db
  [x] local/docs-repo

Verification Results
  Resource          Type            Check       Status  Detail
  ----------------  --------------  ----------  ------  ----------------------------------
  local/api-repo    git-checkout    integrity   pass    All refs valid
  local/staging-db  local/database  connection  error   Connection refused (port 5432)
  local/docs-repo   git-checkout    integrity   pass    All refs valid

[ERROR] 1 of 3 resources failed verification
**What this produces in `color` format:**

Verifying resources  [3/3]
  [x] local/api-repo
  [!] local/staging-db
  [x] local/docs-repo

Verification Results
  Resource          Type            Check       Status  Detail
  ----------------  --------------  ----------  ------  ----------------------------------
  local/api-repo    git-checkout    integrity   pass    All refs valid
  local/staging-db  local/database  connection  error   Connection refused (port 5432)
  local/docs-repo   git-checkout    integrity   pass    All refs valid

[ERROR] 1 of 3 resources failed verification
**What this produces in `yaml` format:**

command: resource verify
status: error
exit_code: 1
data:
  verification_results:
    - Resource: local/api-repo
      Type: git-checkout
      Check: integrity
      Status: pass
      Detail: All refs valid
    - Resource: local/staging-db
      Type: local/database
      Check: connection
      Status: error
      Detail: "Connection refused (port 5432)"
    - Resource: local/docs-repo
      Type: git-checkout
      Check: integrity
      Status: pass
      Detail: All refs valid
timing:
  duration_ms: 2840
messages:
  - level: error
    text: "1 of 3 resources failed verification"
In all formats, the concurrent verification produces a complete result set with partial failures clearly visible. The producer code uses `return_exceptions=True` on `asyncio.gather` to ensure all verifications complete even if some fail, and the error handling within each `verify_one` coroutine ensures that failures are recorded as table rows rather than causing the entire command to abort. ## Behavior ### Automation Profiles Automation profiles determine which phase transitions happen automatically and which require human approval. #### Overview An **automation profile** is a named collection of boolean flags that controls which tasks are automated vs. require human approval. Profiles follow the same `/` naming convention as actors, tools, skills, and other entities. Profiles are managed via the `agents automation-profile` CLI commands. ##### Automatable Tasks Each automation profile specifies a true/false value for each of the following automatable tasks: | Flag | Description | When `true` | When `false` | |------|-------------|-------------|--------------| | `auto_strategize` | Automatically enter Strategize after `plan use` | Strategize begins immediately | System pauses; user must run `agents plan execute` to start Strategize | | `auto_execute` | Automatically proceed from Strategize to Execute | Execute begins when Strategize completes | System pauses after Strategize; user reviews strategy and runs `agents plan execute` | | `auto_apply` | Automatically proceed from Execute to Apply | Apply begins when Execute completes | System pauses after Execute; user reviews diffs and runs `agents plan apply` | | `auto_decisions_strategize` | Automatically make decisions during Strategize | Strategy actor makes all decisions autonomously | System pauses at each decision point for user input | | `auto_decisions_execute` | Automatically make decisions during Execute | Execution actor makes all decisions autonomously | System pauses at each decision point for user input | | `auto_validation_fix` | Automatically attempt to fix validation failures | Execution actor self-fixes failing required validations within strategy bounds | System pauses on validation failure; user must provide guidance | | `auto_strategy_revision` | Automatically revise strategy when Execute cannot solve within constraints | System re-runs Strategize for affected subtree | System pauses and asks user whether to revise the strategy | | `auto_child_plans` | Automatically spawn and execute child plans | Child plans are created and executed without pausing | System pauses before spawning each child plan for approval | | `auto_retry_transient` | Automatically retry on transient failures (network, timeout, rate-limit) | System retries automatically with backoff | System pauses and asks user to retry or abort | | `auto_checkpoint_restore` | Automatically restore from checkpoint on failure | System rolls back to last checkpoint and retries | System pauses; user decides whether to restore or intervene | | `require_sandbox` | Require sandbox isolation for Execute phase | Execute must run in a sandbox (worktree, container, etc.) | Sandbox is optional; Execute may modify resources directly | | `require_checkpoints` | Require checkpointing during Execute | Tools must create checkpoints before writes | Checkpointing is optional | | `allow_unsafe_tools` | Allow execution of tools marked as unsafe | Unsafe tools can be invoked | Unsafe tools are blocked; only safe tools are allowed | ##### Built-in Automation Profiles CleverAgents ships with six built-in automation profiles. Built-in profiles use no namespace prefix. | Flag | `locked-down` | `manual` | `supervised` | `trusted` | `autonomous` | `full-auto` | |------|:---:|:---:|:---:|:---:|:---:|:---:| | `auto_strategize` | - | - | ✓ | ✓ | ✓ | ✓ | | `auto_execute` | - | - | - | ✓ | ✓ | ✓ | | `auto_apply` | - | - | - | - | - | ✓ | | `auto_decisions_strategize` | - | - | ✓ | ✓ | ✓ | ✓ | | `auto_decisions_execute` | - | - | - | ✓ | ✓ | ✓ | | `auto_validation_fix` | - | - | - | ✓ | ✓ | ✓ | | `auto_strategy_revision` | - | - | - | - | ✓ | ✓ | | `auto_child_plans` | - | - | - | ✓ | ✓ | ✓ | | `auto_retry_transient` | - | - | ✓ | ✓ | ✓ | ✓ | | `auto_checkpoint_restore` | - | - | - | - | ✓ | ✓ | | `require_sandbox` | ✓ | ✓ | ✓ | ✓ | ✓ | - | | `require_checkpoints` | ✓ | ✓ | ✓ | ✓ | ✓ | - | | `allow_unsafe_tools` | - | - | - | - | - | ✓ | **`locked-down`**: Maximum human control. Every phase transition, every decision, every child plan requires explicit human approval. Sandbox and checkpoints are mandatory. Unsafe tools are blocked. Use for: critical production systems, first-time exploration of an unfamiliar codebase, high-risk infrastructure changes. **`manual`**: Human drives all phase transitions but decisions within Strategize and Execute are still manual. Similar to `locked-down` but serves as the default starting point. Use for: new users learning the system, sensitive projects, regulatory environments. **`supervised`**: Strategize runs automatically with autonomous decisions, but the system pauses before Execute for human review of the strategy. Transient failures are retried automatically. Use for: projects where you trust the planning but want to review before execution begins. **`trusted`**: Strategize and Execute run automatically with autonomous decisions. Validation failures are self-fixed. Child plans are spawned automatically. The system pauses only before Apply for human review of the final diffs. Use for: day-to-day feature development, routine refactoring, test generation. **`autonomous`**: Everything runs automatically except Apply. The system can even revise its own strategy if execution hits a wall, and restore from checkpoints on failure. Use for: well-understood projects, batch operations, tasks with strong invariant coverage. **`full-auto`**: Complete end-to-end automation including Apply. No sandbox or checkpoint requirements. Unsafe tools are allowed. Use for: low-risk routine tasks (dependency updates, documentation generation, formatting), CI/CD pipeline integration, trusted batch operations with rollback capabilities. ##### Profile Precedence Automation profiles are determined using this precedence (highest to lowest): 1. **Plan-level**: Explicitly set via `--automation-profile` on `agents plan use` 2. **Action-level**: Set on the action via `--automation-profile` on `agents action create` 3. **Project-level**: Set via `agents config set automation-profile --project ` 4. **Global-level**: Set via `agents config set automation-profile ` The **effective profile** for a plan is resolved at the moment of `agents plan use`. Once resolved, the profile is **locked to that plan** — subsequent changes to project or global profiles do not affect running plans. ##### Child Plan Profile Inheritance Child plans inherit the parent plan's effective automation profile. If the parent's profile is changed explicitly after creation, new child plans use the new profile while already-running child plans retain their original profile. ##### Custom Automation Profiles Custom profiles are created via YAML configuration files and registered with `agents automation-profile add`:

# File: profiles/careful-auto.yaml
name: local/careful-auto
description: "Autonomous execution with mandatory sandbox and manual apply"
flags:
  auto_strategize: true
  auto_execute: true
  auto_apply: false
  auto_decisions_strategize: true
  auto_decisions_execute: true
  auto_validation_fix: true
  auto_strategy_revision: false
  auto_child_plans: true
  auto_retry_transient: true
  auto_checkpoint_restore: true
  require_sandbox: true
  require_checkpoints: true
  allow_unsafe_tools: false

agents automation-profile add --config ./profiles/careful-auto.yaml
##### Semantic Escalation Even when a profile sets a task to automatic, the system may still escalate to the user when confidence is low. Semantic escalation is orthogonal to automation profiles — it provides a safety net that works within any profile:

class AutonomyController:
    def assess_decision_confidence(self, decision, context):
        factors = {
            'past_success_rate': self.get_historical_success(decision.type),
            'codebase_familiarity': self.get_familiarity_score(context.project),
            'risk_assessment': self.evaluate_risk(decision),
            'invariant_complexity': self.analyze_invariants(decision)
        }
        
        confidence = self.compute_confidence(factors)
        
        if confidence < self.threshold:
            # Even in autonomous profiles, critical decisions escalate
            return RequestHumanGuidance(decision, factors)
        
        return ProceedAutonomously(decision)
##### Progressive Trust Building New users typically follow this progression: 1. Start with `manual` to understand system behavior 1. Move to `supervised` as confidence in the planning phase builds 1. Adopt `trusted` for routine development tasks 1. Enable `autonomous` for well-understood projects with strong invariant coverage 1. Use `full-auto` for low-risk batch operations or CI/CD integration ### Validation and Guardrails #### Plan generation validation The validation logic is stubbed and must be implemented. The spec should require: * validate action schema * validate actor availability * validate required skills exist * validate permission policy * validate rollback feasibility (if enabled) * validate project resource accessibility This prevents "plan runs with fake providers" and other surprises. #### Cost / rate limits Future concerns: * API call limits * cost caps So CleverAgents should define: * per-plan budgets * per-session budgets * per-org budgets * per-actor max tool calls / max retries ### Correcting Plans (Core Feature) Correcting plans is where CleverAgents becomes more than "a fancy prompt runner." #### The Goal When a plan makes a wrong decision early, we want to: * correct the decision, * recompute only the affected subtree, * preserve unaffected work. This is explicitly described: "redo everything below that decision, not the entire code base." #### Decision Tree Representation Every plan records (see Decision Data Model section): * decisions (choice points) - created during Strategize, including `invariant_enforced`, `subplan_spawn`, and `subplan_parallel_spawn` decisions * dependencies (which later work depended on that decision) * child plans spawned because of that decision - populated during Execute * artifacts generated under that branch This makes plan runs auditable and correctable. #### Two Correction Modes 1. **Revert-from-history correction (`--mode=revert`)** * Find the decision point in the tree * Roll back all changes (code and non-code) to that point * Re-run from that decision point forward * Keep old execution artifacts for comparison * Potentially expensive if high up in the tree 2. **Add-at-end correction (`--mode=append`)** * Leave history intact * Append a new plan at the end that fixes the outcome * Cheaper and safer sometimes * Does not rewrite history #### Correction Flow (Revert Mode) When user requests correction at Decision B: 1. **Mark for Correction**

   Decision B.superseded_by = new_decision_id
   
2. **Identify Downstream Impact** - Recursively collect all decisions that depend on Decision B (including `invariant_enforced`, `subplan_spawn`, and `subplan_parallel_spawn` decisions) - Collect all child plans spawned from those decisions - These form the "affected subtree" 3. **Rollback Resources** - For each affected decision's `artifacts_produced`: - Rollback to the checkpoint before that artifact was created - For affected child plans: - Rollback their sandboxes entirely 4. **Preserve for Comparison** - Archive the original subtree's artifacts - Create a `CorrectionAttempt` record linking old and new 5. **Re-execute from Decision Point** - Restore context to `Decision B.context_snapshot` - User provides new guidance/correction - Re-run execution from that point - New decisions get `is_correction: true, corrects_decision_id: B` 6. **Apply as Normal** - The corrected plan goes through normal Apply gating - Diff shows changes from the correction #### History Cleanup **History can only be flagged for cleanup after a plan is Applied.** Once a plan is applied: - It can no longer be rolled back - Old correction artifacts can be archived or deleted based on retention policy - The decision tree is preserved for audit purposes #### CLI Commands for Correction

# View decision tree
agents plan tree <plan_id>
agents --format=json plan tree <plan_id>  # For visualization tools

# Inspect a specific decision
agents plan explain <decision_id>
# Shows: question, chosen option, alternatives, rationale, downstream impact

# Correct via revert-and-replay
agents plan correct <decision_id> --mode=revert --guidance "<what the decision should be>"
# Re-executes from that point with the new guidance

# Correct via append (add fix at end)  
agents plan correct <decision_id> --mode=append --guidance "<description of the fix>"
# Creates a new child plan to fix the outcome without rewriting history

# Compare old vs new after correction
agents plan diff --correction <correction_attempt_id>
#### Correction Safety Corrections always: * Create a new attempt revision (increment `plan.attempt`) * Preserve old artifacts for diff/compare * Run execute in sandbox again * Require apply gating again * Never modify already-applied changes This keeps history reproducible and prevents accidental destructive edits. ### Human-in-the-Loop Collaboration Even though the direction is "more autonomous," the transcript explicitly recognizes that real workflows require engineers to collaborate with the system, editing code while it works, and using better UX integration (TUI/web/IDE). So CleverAgents should aim for: * visibility: what is it doing now? * interruptibility: pause/cancel/retry * editability: allow user to modify strategy before execute * reconciliation: detect if user changed sandbox files mid-run and handle it ### UI / Interaction Model #### CLI-first + TUI + Web App + IDE The system intends to be CLI-first, with: * a TUI built using Textual, * which can generate a web app "for free," * and later an IDE plugin that embeds the TUI in the IDE. This implies a "single UI codebase" model: * same underlying view logic, * multiple frontends. #### Plan Tree Visualization The TUI should show: * plan list * plan details * plan tree (ASCII) * diff view * approvals And should later allow exporting the tree as image (PNG) or JSON for other visualization tools. ## Additional Recommended Sections (Missing from Draft but Important) ### Storage and Persistence CleverAgents should define where each concept lives: * Actions: stored in a registry (local files or server DB) * Actors: stored similarly (config files + DB indexing) * Projects: stored locally or on server * Plans: stored in plan DB with full logs * Context indexes: vector store / graph store / SQLite * Artifacts: filesystem or object storage ### Observability To debug large plans: * every phase should emit events * every actor call should log prompt/context references * every tool call should log resource access (with parent skill noted) * every checkpoint should be recorded ### Security Model * sandbox isolation * resource-level ACLs * prompt injection mitigations (server mode) * secret management (API keys, DB credentials) * audit logs for apply ### Extensibility * plugin system for skills * custom node types * action templates * actor templates (noted as missing currently) ## Summary of Key Intended Behaviors (If You Only Read One Section) * Plans follow **Action → Strategize → Execute → Apply** with **automation profiles** controlling transitions and decision automation. * **Strategize is read-only** and produces a strategy + blueprint. * **Execute happens in a sandbox**, can spawn child plans, and should support checkpoints/rollback when enabled. * **Apply commits changes** from sandbox to real project after review/validation. * Actors are **hierarchical**: an actor can be a single agent or an entire graph. * Graph nodes can be **actors or tools** (tools are provided by referenced skills, which aggregate tools from MCP servers, Agent Skills folders, built-ins, and custom code). * Context should evolve toward **hot/warm/cold tiers** and **actor-specific context views**. * The system is designed for large tasks where the user can **correct a decision** and only recompute downstream work, visualizable as a plan decision tree. The system can handle Firefox-scale projects not through magic, but through: - **Hierarchical decomposition** breaking massive tasks into bounded work - **Persistent decision graphs** maintaining context across any scale - **Isolated execution** preventing cascading failures - **Semantic validation** catching errors before propagation - **Progressive automation** building trust through incremental success Future details to add to this document: * a canonical **JSON/YAML schema** for Actions, Actors, Projects, Plans, Skills, and Context Views, * a **CLI command reference** (every command, flags, examples), * and a set of **end-to-end example workflows** (single project, multi-project, infra task, paper-writing task) consistent with this spec. ## Work Remaining to Make CleverAgents Fully Functional This section describes what remains to be done to bring the current CleverAgents codebase up to the specification. **Last Updated:** February 6, 2026 ### Current State Assessment #### What's Implemented Based on analysis of the current codebase: 1. **Plan Lifecycle Foundation** - The 4-phase lifecycle (Action → Strategize → Execute → Apply) is partially implemented in `plan_lifecycle_service.py` 2. **Database Models** - Models exist for projects, plans, contexts, changes, and actors 3. **LangGraph Integration** - Graph-based workflow support exists but is not fully connected to the plan workflow 4. **Reactive System** - A reactive system with stream routing is present 5. **Actor System** - Actor models and services exist but lack full behavioral definitions 6. **Basic Context Analysis** - Simple context loading and analysis capabilities 7. **Change Tracking** - Basic `Change` and `ChangeSet` models exist #### What's Missing Critical gaps between the specification and current implementation: 1. **No Resource Abstraction** - The unified resource layer for files, databases, APIs is entirely missing 2. **No Sandboxing** - Execute phase writes directly to files without isolation 3. **No Decision Tree** - No decision tracking, storage, or correction mechanism 4. **No Skills/MCP Integration** - No skill registry, skill YAML parsing, tool adapters, or MCP protocol support 5. **Single-File Limitation** - Hard-coded to generate exactly one file per plan 6. **Text-Based Code Generation** - Still parsing LLM output instead of tool-based approach 7. **No Checkpointing** - No rollback or checkpoint capabilities 8. **No Code Intelligence** - Missing indexing, vector search, and RDF graph store 9. **No Context Tiers** - No hot/warm/cold memory architecture 10. **Limited Validation** - Basic stub validation instead of semantic checks ### Work Items by Priority ### 1) Implement Tool-Based Resource Modification (Critical Foundation) #### Problem The current system generates code as a single text blob that gets written to one file. The specification requires a modern tool-based approach where LLMs invoke discrete operations on resources. #### Implementation Steps 1. **Create Resource Abstraction Layer**

   # New modules needed:
   src/cleveragents/domain/models/resources.py
   src/cleveragents/domain/resources/handlers.py
   src/cleveragents/domain/resources/sandbox.py
   
2. **Implement Built-in Tools (via built-in skills)**

   # Core file operations (provided by built-in skill groups)
   read_file(path: str) -> str
   write_file(path: str, content: str) -> None
   edit_file(path: str, changes: list[Edit]) -> None
   delete_file(path: str) -> None
   move_file(src: str, dst: str) -> None
   create_directory(path: str) -> None
   list_files(pattern: str) -> list[str]
   search_files(pattern: str, content_pattern: str) -> list[Match]
   
3. **Connect Tools to ChangeSet** - Each tool invocation that modifies resources creates a `Change` record - ChangeSet accumulates these changes during execution - No more parsing LLM text output for code #### Estimated Effort: 2-3 weeks ### 2) Implement Sandbox Infrastructure (Critical for Safety) #### Problem Execute phase currently writes directly to the project. The specification requires all changes happen in an isolated sandbox that can be reviewed before applying. #### Implementation Steps 1. **Define Sandbox Interface**

   class Sandbox(Protocol):
       def create() -> SandboxRef
       def read(path: str) -> Content
       def write(path: str, content: Content) -> Change
       def diff() -> DiffView
       def commit() -> None
       def rollback() -> None
   
2. **Implement Sandbox Strategies** - `GitWorktreeSandbox` - For git repositories (preferred) - `FilesystemCopySandbox` - For non-git projects - `TransactionSandbox` - For databases - `NoOpSandbox` - For non-sandboxable resources 3. **Lazy Sandbox Creation** - Only create sandboxes when resources are accessed - Each plan gets its own sandbox namespace #### Estimated Effort: 2 weeks ### 3) Build Decision Tree System (Core Innovation) #### Problem No decision tracking exists. The specification's key innovation is recording every decision with full context, enabling correction without full re-execution. #### Implementation Steps 1. **Create Decision Models**

   -- New tables needed
   CREATE TABLE decisions (
       decision_id TEXT PRIMARY KEY,  -- ULID
       plan_id TEXT NOT NULL,
       parent_decision_id TEXT,
       decision_type TEXT NOT NULL,
       question TEXT NOT NULL,
       chosen_option TEXT NOT NULL,
       alternatives_considered TEXT,  -- JSON array
       context_snapshot TEXT NOT NULL,  -- JSON
       created_at TEXT NOT NULL
   );
   
2. **Implement Decision Recording** - Every choice during Strategize creates a Decision record - Capture complete context snapshot with each decision - Track downstream dependencies 3. **Build Correction Mechanism**

   agents plan correct <decision_id> --mode=revert --guidance "<new decision>"
   
- Mark decision as superseded - Recompute only affected subtree - Preserve unaffected work #### Estimated Effort: 3 weeks ### 4) Implement Code Intelligence System (Scalability Enabler) #### Problem Current context is limited to a few hundred characters from a few files. Large codebases require intelligent context discovery. #### Implementation Steps 1. **Eager Indexing on Resource Add**

   def on_resource_added(resource: Resource):
       # Index immediately when resource added to project
       index_text_content(resource)      # Full-text search
       generate_embeddings(resource)     # Vector embeddings
       build_knowledge_graph(resource)   # RDF triples
   
2. **Three-Index Architecture** - **Text Index**: Tantivy/SQLite FTS for exact matches - **Vector Index**: FAISS/Qdrant for semantic search - **Graph Store**: RDF store for relationships 3. **Tiered Context System** - **Hot**: Current working set (in LLM context) - **Warm**: Recent decisions and search results - **Cold**: Historical data and patterns #### Estimated Effort: 4 weeks ### 5) Complete Actor System with Behavioral Definitions #### Problem Actors exist but don't define behavior beyond model selection. The specification requires actors to be composable graphs with tools, memory, and context policies. #### Implementation Steps 1. **Extend Actor Configuration**

   actors:
     my_strategist:
       type: graph
       config:
         provider: anthropic
         model: claude-3-opus
         memory_policy: per_plan
         context_view: architect # High-level view
       skills:
         - local/file-ops             # provides read_file
         - local/code-intelligence    # provides search_code, analyze_dependencies
       routes:
         strategize:
           entry_point: analyze
           nodes:
             - name: analyze
               type: llm
             - name: plan
               type: llm
   
2. **Implement Tool Access Policies** - Strategy actors: read-only tools - Execute actors: read/write within sandbox - Apply actors: commit tools 3. **Actor-Specific Context Views** - Strategist: Architecture, dependencies, patterns - Executor: Implementation details, specific files - Reviewer: Diffs, tests, risk analysis #### Estimated Effort: 2 weeks ### 6) Resource System, Resource Types, and Resource Registry #### Problem Resources are currently defined inline within projects. The specification requires resources to be independently registered first-class entities with a type system, DAG relationships (physical/virtual), auto-discovery, and content-identity tracking. Tools need resource bindings to declare and resolve their resource dependencies. #### Implementation Steps 1. **Resource Type Registry and CLI** - Implement 24 built-in resource types: 15 physical (`git-checkout`, `git`, `git-remote`, `git-branch`, `git-tag`, `git-commit`, `git-tree`, `git-tree-entry`, `git-stash`, `git-submodule`, `fs-mount`, `fs-directory`, `fs-file`, `fs-symlink`, `fs-hardlink`) and 9 virtual (`file`, `directory`, `symlink`, `commit`, `branch`, `tag`, `remote`, `submodule`, `tree`) - Implement resource type YAML parsing and validation for custom types - Implement `agents resource type add [--update]/remove/list/show` commands - Implement dynamic CLI subcommand registration (custom types create new `agents resource add` subcommands) 2. **Resource Registry and CLI** - Implement `agents resource add /remove/list/show/tree` commands - Implement Resource Registry persistence in database - Implement DAG parent/child relationships with type constraints and cycle detection - Implement `agents resource link-child`/`unlink-child` for manual DAG management 3. **Auto-Discovery System** - Implement handler-driven child resource discovery (git-checkout discovers git + fs-directory worktree root; git discovers remotes, branches, tags, commits, stashes, submodules; git-commit discovers root git-tree; git-tree discovers tree entries + subtrees; fs-mount discovers root fs-directory; fs-directory discovers subdirectories, files, symlinks, hardlinks) - Implement resource reuse during discovery (link existing resources instead of duplicating) - Implement refresh mechanism for keeping discovered children up to date 4. **Physical/Virtual Resource Model** - Implement content hashing for identity tracking - Implement virtual resource linking (shared virtual parents for identical physical resources) - Implement divergence detection (unlink when content changes) 5. **Project-Resource Linking** - Replace `agents project add-resource`/`remove-resource` with `link-resource`/`unlink-resource` - Implement project-level overrides (read-only, alias) - Migrate any existing inline resource data to Resource Registry 6. **Resource Handlers** - Implement `GitCheckoutHandler` with auto-discovery, sandbox creation, checkpoint support - Implement `GitHandler` for git repository structure discovery (remotes, branches, tags, commits, stashes, submodules) - Implement `GitObjectHandler` for git-commit, git-tree, git-tree-entry (read-only access to git objects) - Implement `GitRefHandler` for git-branch, git-tag, git-stash (read/write ref manipulation) - Implement `GitConfigHandler` for git-remote, git-submodule (read-only config access) - Implement `FilesystemHandler` with auto-discovery for fs-mount, fs-directory, fs-file, fs-symlink, fs-hardlink - Implement handler plugin system for custom resource types #### Estimated Effort: 5 weeks ### 7) MCP Integration, Tool System, Skill System, and Tool-Resource Bindings #### Problem No tool or skill abstraction exists. The specification defines tools as independently registered operations managed via `agents tool` CLI commands, and skills as namespaced collections of tools managed via `agents skill` CLI commands. Tools can be sourced from MCP servers, Agent Skills folders, built-ins, and custom code. Tools also need resource bindings to declare and resolve their dependencies on resources. #### Implementation Steps 1. **Tool Registry and CLI** - Implement tool YAML parsing and validation (including `resources` section for resource slots) - Implement `agents tool add [--update]/remove/list/show` commands - Implement Tool Registry persistence in database - Implement anonymous tool support (inline definitions without registration) 2. **Skill Registry and CLI** - Implement skill YAML parsing and validation - Implement `agents skill add [--update]/remove/list/show/tools` commands - Implement hierarchical skill composition (includes) with per-tool metadata overrides - Implement Skill Registry persistence in database - Implement named tool references (resolving from Tool Registry) and anonymous inline tools 3. **Tool-Resource Binding System** - Implement resource slot parsing and validation in tool YAML - Implement three binding modes: contextual, static, parameter - Implement binding resolution at activation time (contextual/static) and invocation time (parameter) - Implement resource type compatibility validation - Implement built-in tool implicit resource bindings - Inject bound resources into `ToolExecutionContext.resources` 4. **MCP Tool Adapter**

   class MCPToolAdapter:
       def wrap_mcp_tool(self, tool) -> Tool:
           # Add sandbox interception
           # Add change tracking
           # Add capability metadata
           # Add resource binding support
   
5. **Tool Capability Metadata**

   class ToolCapability:
       read_only: bool
       write_scope: list[str]       # References resource slot names
       checkpointable: bool
       idempotent: bool
       side_effects: list[str]
   
6. **External MCP Server Support**

   # In tool or skill YAML configuration
   mcp_servers:
     - name: github
       command: "npx @anthropic/mcp-github"
       env: {GITHUB_TOKEN: "${GITHUB_TOKEN}"}
   
7. **Agent Skills Adapter** - Implement SKILL.md frontmatter parsing for discovery - Implement progressive disclosure (metadata → instructions → resources) - Implement sandboxed script execution 8. **Metadata Override System** - Implement shallow-merge override logic for tool capability metadata - Support overrides at skill-level (tool refs), include-level (tool_overrides), and actor graph node-level - Built-in tool metadata is not overridable #### Estimated Effort: 5 weeks ### 8) Implement Validation and Semantic Error Prevention #### Problem Current validation is a stub. The specification requires multi-layer semantic validation. #### Implementation Steps 1. **Decision-Time Validation** - Validate choices during Strategize - Check alternatives for feasibility - Record validation in decision metadata 2. **Execution-Time Guards**

   # Actor references a skill containing validation tools
   skills:
     - local/semantic-validators   # validate_api_compatibility,
                                   # check_invariants, verify_test_coverage
   
3. **Project-Specific Validation**

   agents project validation add --description "Run tests" --required my-api "pytest"
   agents project validation add --description "Lint check" --required my-api "ruff check ."
   
#### Estimated Effort: 2 weeks ### 9) Connect LangGraph to Plan Lifecycle #### Problem The reactive/LangGraph infrastructure exists but isn't connected to the main plan workflow. #### Implementation Steps 1. **Create Unified Plan Graph**

   class PlanLifecycleGraph:
       def strategize_subgraph(self) -> StateGraph
       def execute_subgraph(self) -> StateGraph
       def apply_subgraph(self) -> StateGraph
   
2. **Wire Phase Transitions** - `use` command triggers strategize graph - `execute` command triggers execute graph - `apply` command triggers apply graph 3. **Remove Linear Pipeline** - Replace tell/build/apply with graph execution - Maintain backward compatibility at CLI level #### Estimated Effort: 1 week ### Implementation Roadmap #### Phase 1: Foundation (8-10 weeks) 1. Tool-based resource modification (critical foundation) 2. Sandbox infrastructure 3. Decision tree system #### Phase 2: Core Systems (10-12 weeks) 4. Code intelligence system 5. Actor system with behavioral definitions 6. Resource system, resource types, and resource registry 7. MCP integration, tool system, skill system, and tool-resource bindings #### Phase 3: Validation and Integration (3 weeks) 8. Validation and semantic error prevention 9. Connect LangGraph to plan lifecycle #### Phase 4: Production Hardening (4 weeks) 10. Checkpointing and rollback 11. Cost controls and rate limiting 12. Security fixes (remove eval, fix async) ### Total Estimated Timeline: 6-7 months ### Key Success Metrics 1. **Multi-file generation**: Can generate a REST API with routes/, models/, tests/ 2. **Safe execution**: All changes happen in sandbox, reviewed before apply 3. **Decision correction**: Can correct a decision and recompute only affected work 4. **Scale to large codebases**: Can work with 10K+ file projects efficiently 5. **Semantic safety**: Catches breaking changes before they're applied ### Migration Strategy The implementation can proceed incrementally: 1. Start with resource abstraction (enables everything else) 2. Add sandboxing to existing execute phase 3. Gradually replace text parsing with tool invocations 4. Build decision tree alongside existing flow 5. Enhance context as indexing comes online This allows the system to remain functional during development while progressively adding the architectural improvements described in the specification. ## CleverAgents Architecture FAQ ### Q: How does CleverAgents handle persistent repository knowledge beyond ephemeral context windows? **What exists today architecturally**: The specification defines a sophisticated multi-tier memory system that goes far beyond ephemeral context windows. At its core is the Decision Tree structure which provides a durable, queryable record of every choice made during planning, along with the complete context that informed those choices. **How the persistent model works in practice**: When a strategy actor analyzes a codebase during the Strategize phase, it doesn't just make decisions in isolation. Each decision creates a comprehensive Decision record that includes:

context_snapshot:
  hot_context_hash: str # Cryptographic hash of the exact context
  hot_context_ref: str # Pointer to the full stored snapshot
  relevant_resources: list[ResourceRef] # Every file/symbol that influenced this decision
  actor_state_ref: str # Complete LangGraph checkpoint
This means when the system decides "refactor the authentication module to use async patterns," it permanently records: - Which files were examined to make that decision - What symbols and dependencies were traced - The exact code state that was analyzed - The reasoning chain that led to this choice - Alternative approaches that were considered but rejected **The three-tier memory architecture enables scale**: 1. **Hot tier**: Immediate working context (what's in the current LLM context window) 2. **Warm tier**: Recent decisions and their contexts from this plan tree - quickly accessible 3. **Cold tier**: Historical decisions from past plans on this codebase - queryable but not in active memory When working on a 50,000 file codebase, the system doesn't need to hold all files in memory. Instead: - Hot context focuses on the immediate task (e.g., 10-20 files for a specific refactoring) - Warm context maintains the decision chain that got us here - Cold context provides historical patterns ("last time we refactored auth, we also had to update these services") **Why this scales to massive codebases**: The key insight is that software development is inherently local - even in huge codebases, individual changes typically touch a bounded set of files. The Decision Tree captures these localities. When converting Firefox to Rust (your example), the system would: 1. Make high-level architectural decisions and enforce invariants (captured as root decision nodes) 2. Decompose into major subsystem conversions (each a `subplan_parallel_spawn` or `subplan_spawn` decision spawning child plans) 3. Each subsystem plan makes decisions about its modules, inheriting applicable invariants 4. Module plans make decisions about individual files At each level, only the relevant context is loaded. The persistent decision graph means we can always reconstruct why we're converting a particular module and what constraints apply from higher-level decisions. **Concrete example of persistence in action**:

Plan: Convert Firefox Renderer to Rust
├── [invariant_enforced] "Maintain API compatibility with existing C++ callers"
├── [invariant_enforced] "All converted modules must pass existing C++ test suites"
├── [strategy_choice] Architecture approach: Start with leaf modules, work inward
│   Context: Analyzed module dependency graph, 2,847 modules total
│   Resources: module_graph.json, architecture_docs.md
│   
├── [subplan_parallel_spawn] Phase 1: Convert utility libraries (no external deps)
│   └── [subplan_spawn] Convert string_utils module
│       └── Plan: 01KH29R8WPKPBHRY7Q0NA9XW86
│           ├── [prompt_definition] "Convert string_utils module to Rust"
│           ├── [invariant_enforced] "Maintain API compatibility with existing C++ callers"
│           ├── [implementation_choice] Use Rust's String type, not custom implementation
│           │   Context: Analyzed 47 string_utils.cpp functions
│           │   Resources: string_utils.cpp, string_utils.h, 12 dependent files
│           │   Rationale: Rust's String provides same guarantees with better ergonomics
│           └── ...
Even months later, we can query: "Why did we use Rust's String type?" and get the exact context and reasoning, without reprocessing the entire codebase. ### Q: How does the system compute task-specific dependency closures for large-scale operations? **What exists today architecturally**: The specification defines multiple mechanisms for computing and maintaining minimal dependency closures. The execution blueprint produced during the Strategize phase doesn't just list steps - it includes a complete dependency graph with explicit scoping for each operation. **How dependency closure computation works**: During the Strategize phase, the strategy actor employs several mechanisms to compute precise dependency closures: 1. **Resource-aware analysis**: The actor uses specialized skills to trace dependencies:

   # Pseudocode of what happens inside a strategy actor
   def compute_closure_for_refactoring(target_module):
       closure = ResourceClosure()
       
       # Direct file dependencies
       closure.add_files(find_imports(target_module))
       closure.add_files(find_includes(target_module))
       
       # Symbol dependencies
       for symbol in extract_exported_symbols(target_module):
           closure.add_files(find_symbol_usage(symbol, scope='project'))
       
       # Test dependencies
       closure.add_files(find_tests_for_module(target_module))
       
       # Build system dependencies
       closure.add_files(find_build_references(target_module))
       
       return closure
   
2. **Hierarchical scoping**: When spawning child plans (via `subplan_spawn` or `subplan_parallel_spawn`), each child plan receives: - An explicit `relevant_resources` list - A `sandbox_strategy` appropriate for those resources - Clear boundaries of what it can and cannot modify - The parent plan's effective invariant view (already reconciled from action, project, and global scopes) 3. **Decision-based tracking**: Each `subplan_spawn` decision records the child plan it creates, and `subplan_parallel_spawn` decisions group parallel child plans:

   # Individual child plan spawn
   decision_type: subplan_spawn
   chosen_option: "Refactor authentication module"
   downstream_plan_ids: ["plan-auth-refactor-123"]
   artifacts_produced: 
     - auth_module_files: ["auth.rs", "auth_test.rs", "auth_types.rs"]
     - api_updates: ["api/v2/login.rs", "api/v2/logout.rs"]
   
   # Parallel group of child plans
   decision_type: subplan_parallel_spawn
   chosen_option: "Convert utility libraries in parallel"
   # Contains subplan_spawn children, each with their own downstream_plan_ids
   
**Concrete example - Converting a subsystem to Rust**: Let's trace how the system handles "Convert Firefox's Network Stack to Rust":

STRATEGIZE PHASE:
1. Analyze network stack structure
   - Identifies 847 C++ files in netwerk/ directory
   - Traces public API surface (237 exported functions)
   - Maps internal dependencies (1,432 internal calls)
   
2. Compute minimal closure for Phase 1 (DNS resolver):
   - Core files: dns_resolver.cpp, dns_cache.cpp, dns_config.cpp (3 files)
   - Direct dependencies: 12 files in netwerk/base/
   - Test files: 8 test files specific to DNS
   - Build files: 2 moz.build files
   - Total closure: 25 files (not 847!)
   
3. Generate execution blueprint with child plans:
   - [subplan_parallel_spawn] DNS module conversions:
     - convert-dns-types: Closure of 5 files (type definitions)
     - convert-dns-cache: Closure of 8 files (cache + tests)  
     - convert-dns-resolver: Closure of 12 files (resolver + integration)
**Why this is tractable even for massive codebases**: The system leverages several key insights about real software: 1. **Modular boundaries exist**: Even in legacy codebases, there are natural boundaries 2. **Changes are incremental**: We don't convert 50,000 files atomically 3. **Dependencies are sparse**: Most modules depend on a small fraction of the codebase 4. **Interfaces are narrow**: Public APIs are much smaller than implementations The Firefox example would decompose into ~1,000 bounded child plans (grouped via `subplan_parallel_spawn` decisions where independent), each touching 10-100 files. The parent plan tracks the overall architecture and enforces invariants, while each child plan maintains its focused closure. **How we prevent closure explosion**: - **Lazy expansion**: Dependencies are traced only as deep as needed for correctness - **Interface-based boundaries**: When possible, work against stable interfaces - **Incremental validation**: Each child plan validates its changes don't break dependents - **Hierarchical merge strategies**: Parent plans resolve conflicts between child plan changes ### Q: What mechanisms enforce global consistency during parallel execution across many files? **What exists today architecturally**: The sandbox model combined with hierarchical plan execution provides strong guarantees about consistency during parallel execution. This isn't just process isolation - it's semantic isolation with intelligent merge strategies. **How the coordination mechanism prevents compound errors**: 1. **Complete isolation during execution**: Each plan executes in its own sandbox, which means:

   Plan A (refactoring auth module):
   - Sandbox A1: Contains only auth/*.cpp, auth_tests/*.cpp
   - Cannot see Plan B's intermediate states
   - Cannot accidentally depend on Plan B's half-done work
   
   Plan B (updating API endpoints):
   - Sandbox B1: Contains only api/*.cpp, api_tests/*.cpp  
   - Makes changes assuming current auth interface
   - Protected from Plan A's intermediate refactoring
   
2. **Resource-specific sandbox strategies provide natural coordination**:

   Git repositories:
     - Strategy: git worktrees
     - Coordination: Git's three-way merge algorithm
     - Conflict detection: Built into Git
     - Rollback: git reset/checkout
   
   Databases:
     - Strategy: Transaction isolation  
     - Coordination: MVCC (multi-version concurrency control)
     - Conflict detection: Serialization failures
     - Rollback: Transaction abort
   
   Cloud Infrastructure:
     - Strategy: Terraform workspaces
     - Coordination: State locking
     - Conflict detection: Resource conflicts in plan
     - Rollback: Previous state restoration
   
3. **Hierarchical merge resolution**: When child plans complete, the parent plan performs intelligent merging:

   def merge_subplan_results(subplan_results):
       # Group by resource type
       by_resource = group_by_resource_type(subplan_results)
       
       # Apply resource-specific merge strategies
        for resource_type, changes in by_resource:
            if resource_type == 'git-checkout':
                merge_git_changes(changes)  # Three-way merge
            elif resource_type == 'fs-mount':
                merge_fs_changes(changes)   # Copy-on-write reconciliation
            elif resource_type.startswith('database'):
                merge_db_changes(changes)   # Sequential application
       
       # Validate merged state
       run_integration_tests()
   
**Concrete example - Preventing cascading failures**: Consider refactoring a shared authentication library used by 15 services:

PARALLEL EXECUTION WITHOUT COORDINATION (what we prevent):
- Service A refactors to async auth → breaks Service B
- Service B compensates with workaround → breaks Service C  
- Service C changes error handling → breaks Services D, E, F
- Cascade of failures!

CLEVERAGENTS COORDINATED EXECUTION:
Parent Plan: Refactor auth library
├── [invariant_enforced] "Auth library public API must remain backward compatible during transition"
├── [subplan_spawn] Plan 1: Update auth library interface
│   Sandbox: Only auth library files
│   Output: New interface definition
│   
├── Barrier: Wait for Plan 1 completion
│   
├── [subplan_parallel_spawn] Plans 2-16: Update each service (in parallel)
│   Each sandbox: Only that service's files
│   Each uses: New interface from Plan 1
│   No inter-service dependencies during execution
│   
└── Merge Phase:
    - Collect all service updates
    - Apply to main branch in order
    - Run integration tests
    - If conflicts: Parent plan resolves using semantic understanding
**Advanced coordination patterns**: 1. **Optimistic concurrency with semantic conflict resolution**:

   Two child plans both modify api/user.rs:
   - Plan A: Adds async fn get_user_profile()
   - Plan B: Adds fn validate_user_permissions()
   
   Merge strategy:
   - Git merge succeeds (different functions)
   - Semantic validation ensures both functions work together
   - Parent plan adds integration glue if needed
   
2. **Checkpoint-based coordination**:

   Execution timeline:
   T1: Plan A creates checkpoint before major refactor
   T2: Plan B creates checkpoint before API changes
   T3: Plan A encounters error, rolls back to T1
   T4: Plan B completes successfully
   T5: Plan A retries with knowledge of B's success
   
3. **Resource locking for critical sections**:

   When modifying shared schema files:
   - Acquire exclusive lock on schema resources
   - Make changes atomically
   - Release lock with new version
   - Other plans rebase on new schema
   
### Q: How does the system proactively prevent semantic errors before they propagate? **What exists today architecturally**: The specification defines multiple layers of proactive error prevention that go far beyond traditional testing. This is a comprehensive defense-in-depth approach that catches semantic errors before they can propagate. **Layer 1: Decision-time validation during Strategize**: Every decision includes semantic validation:

Decision: Refactor payment module to async
alternatives_considered:
  - "Convert to async/await patterns" (chosen)
  - "Use thread pool with channels" (rejected: doesn't integrate with async ecosystem)
  - "Keep synchronous with timeout" (rejected: doesn't solve core latency issue)
confidence_score: 0.85
validation_performed:
  - Checked all payment API consumers can handle async
  - Verified database driver supports async operations
  - Confirmed no regulatory requirement for sync processing
**Layer 2: Execution-time semantic guards**: The execution actor uses a tool node that references the independently registered `local/validate-api-compat` tool:

actors:
  code_executor:
    type: graph
    skills:
      - local/semantic-validators    # Contains validation tools for LLM tool-calling
    nodes:
      - name: semantic_validator
        type: tool
        tool: local/validate-api-compat # Named tool from Tool Registry
The `local/validate-api-compat` tool (independently registered via `agents tool add`) performs semantic validation — not just syntax checking. It extracts API signatures, finds breaking changes, checks affected consumers, and either auto-migrates or raises a `SemanticError` for manual review. **Layer 3: Invariant enforcement through the decision tree**: Invariants are attached at four scopes (global, project, action, plan) and managed via the unified `agents invariant` command or `--invariant` flags on creation commands. When a plan enters Strategize, the **Invariant Reconciliation Actor** (set via `--invariant-actor` on actions/plans/projects, or globally via `agents config set invariant-actor`) computes the effective invariant view by applying precedence rules (plan > project > global) to resolve conflicts. Each effective invariant is recorded as an `invariant_enforced` decision, making them visible, correctable, and auditable:

# The system collects, reconciles, and enforces semantic invariants
class InvariantEnforcer:
    def compute_effective_invariants(self, plan):
        """Compute the effective invariant view using the Invariant Reconciliation Actor."""
        raw = self.collect_all_invariants(plan)
        reconciler = (
            self.get_plan_invariant_actor(plan)
            or self.get_project_invariant_actor(plan)
            or self.get_global_invariant_actor()
        )
        # Apply precedence: plan > project > global
        return reconciler.reconcile(raw, precedence=['plan', 'project', 'global'])
    
    def collect_all_invariants(self, plan):
        """Collect invariants from all scopes accessible to this plan."""
        invariants = []
        invariants.extend(self.get_global_invariants())
        for project in plan.projects:
            invariants.extend(self.get_project_invariants(project))
        invariants.extend(self.get_action_invariants(plan.action))
        invariants.extend(self.get_plan_invariants(plan))
        return invariants
    
    # Example invariants at different scopes:
    # Global:  "Payment processing must be idempotent"
    # Project: "Database transactions must complete within 5 seconds"
    # Action:  "Test files must not import production secrets"
    # Plan:    "All API calls over TCP must be mocked"
    
    def check_invariant_preservation(self, changes, enforced_invariants):
        for invariant in enforced_invariants:
            if not self.verify_invariant(invariant, changes):
                return InvariantViolation(invariant, changes)
        return Success()
**Layer 4: Predictive error prevention through pattern matching**: The system learns from past failures:

Error Pattern Database:
  - pattern: "Async conversion in payment module"
    historical_failures:
      - "Race condition in payment confirmation"
      - "Timeout handling breaks idempotency"
    preventive_checks:
      - "Add explicit transaction boundaries"
      - "Verify idempotency keys are preserved"
      - "Check distributed lock acquisition"
**Concrete example - Preventing a subtle distributed systems bug**: Scenario: Refactoring a service to use event sourcing:

PROACTIVE CONTAINMENT IN ACTION:

1. Strategy Phase Semantic Analysis:
   - Decision: "Convert order service to event sourcing"
   - Semantic check: "Event sourcing requires eventual consistency"
   - Identifies: 3 services assume immediate consistency
   - Adds decision: "Update dependent services for eventual consistency"

2. Execution Phase Invariant Checking:
   - Detects: PaymentService.chargeCard() called after OrderCreated event
   - Semantic issue: Payment before order confirmation violates business rules
   - Automatic fix: Insert OrderConfirmed event requirement
   
3. Validation Node Catches Edge Case:
   - Discovers: Audit service expects synchronous order numbers
   - Impact: Async events break compliance reporting
   - Resolution: Add audit event buffer with guaranteed ordering
   
4. Pre-Apply Semantic Verification:
   - Simulates production event flow
   - Detects: Under high load, events can arrive out of order
   - Adds: Event ordering guarantees via vector clocks
**Why this prevents issues that traditional testing misses**: Traditional tests check "does the code work?" Our semantic containment asks: - Does it preserve business invariants? - Does it maintain architectural patterns? - Does it respect distributed systems principles? - Does it handle the edge cases we've seen before? **Integration with Definition of Done (DoD)**: Each plan's DoD includes semantic requirements:

definition_of_done:
  must:
    - "All API changes maintain backward compatibility"
    - "No increase in p99 latency"
    - "Audit trail remains complete"
  should:
    - "Improve code coverage by 10%"
    - "Reduce cyclomatic complexity"
  may:
    - "Optimize for memory usage"
The validation nodes enforce these semantics, not just test passage. ### Q: How does the system balance human supervision with autonomous operation? **What exists today architecturally**: The specification defines a comprehensive automation profile system with 13 individual boolean flags controlling every aspect of human-vs-automated operation. This isn't a binary human/AI split — it's a fine-grained matrix that can be adjusted per task, per project, or per organization. **How the automation profiles work in practice**:

locked-down Profile:
  - Every phase transition pauses for human action
  - Every decision point pauses for human input
  - User sees: Context, alternatives, recommendation
  - User provides: Explicit choice or custom guidance
  - Use case: Critical production changes, first-time codebase exploration

supervised Profile:
  - Strategize runs automatically with autonomous decisions
  - System pauses before Execute for human review of strategy
  - Transient failures retried automatically
  - Use case: Projects where you trust planning but want to review before execution

trusted Profile:
  - Strategize and Execute run automatically with autonomous decisions
  - Validation failures self-fixed within strategy bounds
  - Child plans spawned automatically
  - System pauses before Apply for human diff review
  - Use case: Normal feature development, refactoring

autonomous Profile:
  - Everything automatic except Apply
  - Can revise its own strategy if execution hits a wall
  - Restores from checkpoints on failure
  - Use case: Well-understood projects, batch operations

full-auto Profile:
  - Complete end-to-end automation including Apply
  - No sandbox or checkpoint requirements
  - Unsafe tools allowed
  - Use case: Routine updates, CI/CD integration
**The decision correction mechanism enables progressive automation**: The `agents plan correct` command is crucial for building trust:

# User observes AI made suboptimal choice
agents plan tree <plan_id>
# Sees: [Decision] "Use REST API for service communication"

# User knows gRPC would be better for this use case
agents plan correct <decision_id> --mode=revert \
  --guidance "Use gRPC instead of REST. This service requires streaming 
             updates and binary protocol efficiency. Set up protocol 
             buffer definitions and generate client/server stubs."

# System:
# 1. Marks original decision as superseded
# 2. Creates new decision with user guidance
# 3. Recomputes ONLY affected downstream decisions
# 4. Preserves all unrelated work
**Progressive trust building through automation profiles**: New users typically follow this progression: 1. Start with `manual` to understand system behavior 2. Move to `supervised` as confidence in planning builds 3. Adopt `trusted` for routine development tasks 4. Enable `autonomous` for well-understood projects 5. Use `full-auto` for low-risk batch operations **Real autonomy through semantic understanding**: True autonomy isn't about removing humans - it's about the system understanding when it needs help:

class AutonomyController:
    def assess_decision_confidence(self, decision, context):
        factors = {
            'past_success_rate': self.get_historical_success(decision.type),
            'codebase_familiarity': self.get_familiarity_score(context.project),
            'risk_assessment': self.evaluate_risk(decision),
            'invariant_complexity': self.analyze_invariants(decision)
        }
        
        confidence = self.compute_confidence(factors)
        
        if confidence < self.threshold:
             if self.profile.auto_decisions_strategize:
                # Even in full automation, critical decisions escalate
                return RequestHumanGuidance(decision, factors)
        
        return ProceedAutonomously(decision)
**Concrete example - Autonomous handling of a complex refactoring**:

Scenario: "Modernize legacy e-commerce system"

INITIAL PLAN (Full Automation Mode):
1. System analyzes 50,000 line codebase
2. Identifies modernization opportunities
3. Creates plan with 47 child plans

AUTONOMOUS EXECUTION WITH SMART ESCALATION:

[subplan_parallel_spawn] Plans 1-15: Update utility functions (executes autonomously)
- Confidence: 0.95 (straightforward transformations)
- Result: Success

[subplan_spawn] Plan 16: Refactor payment processing
- Confidence: 0.4 (critical business logic)
- [invariant_enforced] "Preserve exact penny rounding behavior"
- Action: ESCALATES to human
- Human provides: "Preserve exact penny rounding behavior"
- Continues autonomously with constraint

[subplan_parallel_spawn] Plans 17-30: UI component updates (executes autonomously)
- Confidence: 0.9 (isolated changes)
- Result: Success

[subplan_spawn] Plan 31: Database schema migration
- Detects: Would require 6-hour downtime
- Action: ESCALATES to human
- Human provides: "Use online migration with feature flags"
- Re-plans with zero-downtime approach

[subplan_parallel_spawn] Plans 32-47: Complete autonomously
**The path to greater autonomy**: The system becomes more autonomous through: 1. **Learning from corrections**: - Every correction teaches the system about user preferences - Patterns emerge: "This team always prefers gRPC for microservices" - Future decisions incorporate these learnings 2. **Building project-specific context**: - Each successful plan adds to project knowledge - System learns codebase patterns, team conventions, business rules - Confidence increases with familiarity 3. **Hierarchical delegation**: - Proven child plan patterns become fully autonomous - Human focuses on high-level decisions - System handles implementation details 4. **Semantic safety nets**: - Comprehensive invariant checking reduces risk - Rollback capabilities provide recovery path - Humans can trust system won't cause catastrophic failures ### Q: What's actually implemented today versus planned for the future? **Concrete implementations in the architecture**: 1. **Decision Tree with Complete Context Capture** - Full schema defined - Storage model specified - Correction mechanism detailed - Query patterns established 2. **Hierarchical Plan System** - Spawning mechanism defined (sequential `subplan_spawn` and parallel `subplan_parallel_spawn`) - Execution semantics specified (child plans are full Plans with their own decision trees) - Invariant inheritance from parent plans, projects, and global scope - Merge strategies documented - Failure handling described 3. **Resource-Aware Sandbox Isolation** - Multiple strategies defined (git worktree, filesystem overlay, transactions) - Lazy sandboxing for efficiency - Resource-specific merge algorithms - Cleanup behavior specified 4. **Multi-Layer Error Prevention** - Decision validation during planning - Semantic validation nodes - Invariant enforcement - Definition of Done checking 5. **Graduated Automation Controls** - Three levels clearly defined - Decision correction without full re-execution - Confidence-based escalation - Progressive trust building **Near-term implementations** (architecture complete, engineering straightforward): 1. **Memory Tier Management** - Hot/warm/cold distinction clear - Context loading patterns defined - Just needs LRU cache and storage backend 2. **Cross-Plan Learning** - Decision history provides training data - Pattern extraction is standard ML - Confidence scoring is well-understood 3. **Cost/Risk Estimation** - Dedicated estimation actor role defined - Historical data provides baselines - Standard prediction problem 4. **Extended Validation Patterns** - Pluggable validation architecture - Project-specific rules as configuration - Industry patterns can be packaged **Research territory** (requires innovation but architecture supports): 1. **Optimal Context Selection for 100K+ file codebases** - Current: Heuristic-based selection - Research: ML-driven relevance ranking - Architecture supports: Any selection algorithm can plug in 2. **Automated Invariant Discovery** - Current: User-defined invariants - Research: Mining invariants from code patterns - Architecture supports: Invariants are just validation rules 3. **Cross-Project Knowledge Transfer** - Current: Project-specific learning - Research: Generalized pattern recognition - Architecture supports: Cold tier can span projects 4. **Fully Autonomous Recovery Strategies** - Current: Rollback and retry with guidance - Research: Automatic error understanding and fixing - Architecture supports: Recovery is just another plan type **Why we can confidently handle Firefox-scale projects**: The architecture doesn't require magical AI breakthroughs. It requires: - **Hierarchical decomposition**: ✓ Fully specified - **Bounded context operations**: ✓ Dependency closure computation defined - **Parallel execution with isolation**: ✓ Sandbox model complete - **Semantic validation**: ✓ Multi-layer approach specified - **Progressive automation**: ✓ Automation profiles and correction defined The difference between handling a 1,000 file project and a 100,000 file project is: - More child plans (hierarchical decomposition handles this) - Larger cold storage (standard database scaling) - Better context selection (improves with use but works with heuristics) - More validation patterns (accumulate over time) This isn't speculative architecture astronautics - it's applying proven distributed systems principles to AI agent coordination. The innovation is in the integration, not in requiring fundamental breakthroughs.