# Actor Configuration Reference

Practical guide to writing actor YAML configuration files with hierarchical
graph examples and common error cases.

**Module:** `cleveragents.actor.schema` / `cleveragents.actor.loader`

Related references:
- [Actor YAML Schema](actors_schema.md) — full field listing
- [Actor Hierarchy Extensions](actor_hierarchy.md) — LSP bindings and tool sources
- [Actor Loader](actors_loading.md) — discovery and caching

---

## Table of Contents

- [Actor Configuration Reference](#actor-configuration-reference)
  - [Basic Structure](#basic-structure)
  - [Hierarchical Graph Example](#hierarchical-graph-example)
  - [Node Types](#node-types)
  - [Graph Topology Rules](#graph-topology-rules)
  - [Error Cases](#error-cases)

---

## Basic Structure

Every actor YAML file must include `name`, `type`, and `description`.
The `name` must follow the `namespace/identifier` format.

```yaml
name: local/my-actor       # Required — namespaced
type: llm                  # Required — llm | tool | graph
description: What it does  # Required
version: "1.0"             # Optional
model: gpt-4               # Required for llm and graph types
```

---

## Hierarchical Graph Example

A graph actor defines a multi-node workflow with conditional routing,
per-node LSP bindings, and tool-source references.

```yaml
name: workflows/dev-pipeline
type: graph
description: Multi-agent development pipeline
version: "1.0"
model: gpt-4

# Actor-level skill and LSP declarations
skills:
  - local/file-ops
  - local/git-ops
lsp:
  - local/pyright

route:
  nodes:
    - id: planner
      type: agent
      name: Planner
      description: Plans the implementation strategy
      config:
        model: gpt-4
        prompt: "Analyze requirements and create a plan."
      # Per-node LSP binding (overrides actor-level default)
      lsp_binding:
        server: local/pyright
        languages:
          - python
        capabilities:
          - diagnostics
      # Per-node tool sources
      tool_sources:
        - type: skill
          name: local/file-ops

    - id: implementer
      type: agent
      name: Implementer
      description: Implements the plan
      config:
        model: gpt-4
        prompt: "Implement the changes per the plan."
      lsp_binding:
        auto: true
      tool_sources:
        - type: skill
          name: local/file-ops
        - type: skill
          name: local/git-ops
        - type: mcp
          name: local/filesystem

    - id: reviewer
      type: subgraph
      name: Code Reviewer
      description: Delegates to the code-review actor
      actor_ref: local/code-reviewer    # namespaced reference to another actor

    - id: gate
      type: conditional
      name: Quality Gate
      description: Routes to reviewer or re-plans based on lint result
      config:
        conditions:
          - check: "state.get('lint_ok') == True"
            route_to: reviewer
          - check: "state.get('lint_ok') == False"
            route_to: planner

  edges:
    - from_node: planner
      to_node: implementer
    - from_node: implementer
      to_node: gate
    - from_node: gate
      to_node: planner     # explicit back-edge for retry path
    - from_node: gate
      to_node: reviewer

  entry_node: planner
  exit_nodes:
    - reviewer
```

---

## Node Types

| Type | Purpose | Key config fields |
|------|---------|-------------------|
| `agent` | LLM-driven reasoning node | `model`, `prompt`, `tools` |
| `tool` | Runs a specific tool call | `tool_name`, `parameters` |
| `conditional` | Routes to different nodes based on state | `conditions[].check`, `conditions[].route_to` |
| `subgraph` | Delegates to another actor entirely | `actor_ref` |

### Conditional Node Routing

Conditional nodes use Python expressions over `state` to route the workflow.
The `route_to` targets are counted as reachable even without explicit edges.

```yaml
- id: branch
  type: conditional
  name: Branch
  description: Decides next step
  config:
    conditions:
      - check: "state.get('success') == True"
        route_to: done
      - check: "state.get('success') == False"
        route_to: retry
```

### Subgraph Node

Subgraph nodes delegate execution to another actor by namespaced name.
Use `actor_ref` (not `config.actor_path`).

```yaml
- id: review_step
  type: subgraph
  name: Review
  description: Runs the review sub-workflow
  actor_ref: local/reviewer    # correct: namespaced actor reference
```

---

## Graph Topology Rules

The loader enforces these constraints on every graph actor:

| Rule | Error trigger |
|------|--------------|
| Unique node IDs | Two nodes share the same `id` |
| Entry node exists | `entry_node` references an `id` not in `nodes` |
| Exit nodes exist | Any `exit_nodes` entry references a missing `id` |
| Edge targets exist | `from_node` or `to_node` reference a missing `id` |
| Nodes are reachable | A node has no path from `entry_node` via edges or conditional routing |
| No cycles | A non-conditional cycle exists in the edge graph |

---

## Error Cases

### Naming Errors

**Actor name missing namespace:**
```yaml
name: my-actor     # wrong — must be namespace/name
```
Error: `name must be in 'namespace/name' format`

Fix: use a namespaced name like `local/my-actor` or `workflows/my-actor`.

---

### LLM Actor Without Model

```yaml
name: local/my-actor
type: llm
description: Assistant
# model: missing
```
Error: `llm and graph types require 'model'`

---

### GRAPH Actor Without Route

```yaml
name: local/pipeline
type: graph
description: Pipeline
model: gpt-4
# route: missing
```
Error: `graph type requires 'route'`

---

### Duplicate Node IDs

```yaml
nodes:
  - id: step_a
    ...
  - id: step_a    # duplicate
    ...
```
Error: `Duplicate node IDs: 'step_a'`

Fix: give each node a unique `id`.

---

### Missing Entry Node

```yaml
route:
  nodes:
    - id: node_a
      ...
  entry_node: node_x    # node_x does not exist
```
Error: `Entry node 'node_x' not found in route.entry_node`

---

### Invalid Edge Target

```yaml
edges:
  - from_node: node_a
    to_node: node_z    # node_z does not exist
```
Error: `Edge to_node 'node_z' not found at route.edges[0]`

---

### Unreachable Node

```yaml
nodes:
  - id: node_a
  - id: node_b
  - id: node_c    # no edge or conditional route_to points here
edges:
  - from_node: node_a
    to_node: node_b
entry_node: node_a
```
Error: `route: nodes 'node_c' are unreachable from entry node 'node_a'`

Fix: add an edge from a reachable node to `node_c`, or remove `node_c` from
`nodes` if it is not needed.

---

### YAML Parse Error

A malformed YAML file produces a parse error with the location:

```
Invalid YAML in bad.yaml at line 3, column 5: mapping values are not allowed here
```

Fix: check the indicated line and correct the YAML syntax.

---

### Schema Validation Error Format

When a YAML file is syntactically valid but fails schema validation, the
loader reports precise field paths:

```
Schema validation failed for actor.yaml:
  type: Input should be 'llm', 'tool' or 'graph'
  route.nodes.0.id: String should match pattern '^[a-zA-Z0-9_-]+$'
  Hint: see docs/reference/actor_config.md for the correct schema format.
```

Each line shows the dotted field path and the validation message.
