# `cleveragents.actor` — Actor System The `actor` package provides the actor registry, YAML configuration schema, loader, and compiler. Actors are the primary execution units in CleverAgents — they encapsulate an LLM, a set of tools, and a LangGraph execution graph. See [ADR-010](../adr/ADR-010-actor-and-agent-architecture.md) and [ADR-031](../adr/ADR-031-actor-abstraction-definition.md) for design rationale. !!! note "Lazy imports" The `actor` package uses lazy imports to avoid pulling in heavy transitive dependencies (LangChain, LangSmith, SQLAlchemy) when only lightweight submodules such as `role_validation` or `schema` are needed. --- ## `ActorConfiguration` ```python from cleveragents.actor import ActorConfiguration ``` Pydantic model representing a fully-parsed actor YAML file. Key fields: | Field | Type | Description | |-------|------|-------------| | `name` | `str` | Actor identifier (namespaced, e.g. `openai/gpt-4o`) | | `entry_node` | `str` | Graph entry node name | | `nodes` | `dict[str, NodeConfig]` | Node definitions | | `edges` | `list[EdgeConfig]` | Graph edges | | `lsp_binding` | `LSPBinding \| None` | Per-node LSP server binding | | `tool_sources` | `list[str]` | Tool source references | --- ## `ActorLoader` ```python from cleveragents.actor import ActorLoader loader = ActorLoader() actors = loader.discover(Path("./actors/")) ``` Discovers and loads actor YAML files from a directory tree. Validates graph reachability (all nodes reachable from `entry_node`) and reports YAML line/column positions on error. ### Methods | Method | Description | |--------|-------------| | `discover(path)` | Scan directory for actor YAML files | | `load(path)` | Load a single actor YAML file | | `validate(config)` | Validate an `ActorConfiguration` | --- ## `ActorRegistry` ```python from cleveragents.actor import ActorRegistry registry = ActorRegistry() registry.register(actor_config) actor = registry.get("openai/gpt-4o") ``` In-memory registry for actor configurations. Thread-safe. --- ## `compile_actor` / `CompiledActor` ```python from cleveragents.actor import compile_actor, CompiledActor compiled: CompiledActor = compile_actor(actor_config, tool_registry) ``` Compiles an `ActorConfiguration` into a runnable `CompiledActor` by resolving tool bindings, building the LangGraph state machine, and attaching LSP/MCP adapters. ### `CompilationMetadata` Carries diagnostics from the compilation step: resolved tools, skipped nodes, and any warnings. --- ## Compilation Errors | Exception | Description | |-----------|-------------| | `ActorCompilationError` | General compilation failure | | `MissingNodeError` | Referenced node not defined | | `InvalidEntryExitError` | Entry/exit node configuration invalid | | `SubgraphCycleError` | Cycle detected in subgraph references | --- ## `InvariantReconciliationActor` **Introduced:** v3.8.0 | **Module:** `cleveragents.actor.reconciliation` The built-in `InvariantReconciliationActor` (`builtin/invariant-reconciliation`) is invoked by `PlanLifecycleService` when a plan enters the Strategize phase. This gate must succeed before Strategize work begins; the lifecycle service persists the reconciled invariant view and reuses it for later Execute or Apply transitions instead of re-running the actor. The actor collects invariants from four scopes, resolves conflicts using specificity-based precedence, records `invariant_enforced` decisions, and returns a reconciled `InvariantSet`. See [ADR-016 — Invariant System](../adr/ADR-016-invariant-system.md) for the design rationale behind invariant reconciliation. ```python from cleveragents.actor.reconciliation import InvariantReconciliationActor actor = InvariantReconciliationActor( invariant_service=container.invariant_service(), decision_service=container.decision_service(), ) result = actor.run(plan_id="01HXYZ...", project_name="local/my-app") # result.reconciled_set — effective InvariantSet # result.conflicts — list[ConflictRecord] with resolution details # result.enforced_decision_ids — list of ULID strings ``` ### Reconciliation Algorithm 1. Collect invariants from four scopes: **global**, **project**, **action**, **plan**. 2. Group by normalised text (case-insensitive, stripped). 3. Detect conflicts between invariants at different scopes. 4. Resolve using specificity: `plan > action > project > global`. Exception: `non_overridable` global invariants always win. 5. Record an `invariant_enforced` decision for each active invariant. 6. Return a reconciled `InvariantSet`. ### `InvariantReconciliationActor` Methods | Method signature | Description | |------------------|-------------| | `collect_invariants(*, plan_id: str \| None = None, project_name: str \| None = None, action_name: str \| None = None) -> ScopeInvariants` | Collect invariants from all four scopes and return them as a `ScopeInvariants` container | | `run(*, plan_id: str, project_name: str \| None = None, action_name: str \| None = None, parent_decision_id: str \| None = None) -> ReconciliationResult` | Execute the reconciliation lifecycle (collect → reconcile → record) and return a `ReconciliationResult` | ### `ReconciliationResult` ```python from cleveragents.actor.reconciliation import ReconciliationResult @dataclass(frozen=True) class ReconciliationResult: reconciled_set: InvariantSet # effective invariants conflicts: list[ConflictRecord] # detected conflicts with resolution details enforced_decision_ids: list[str] # ULIDs of invariant_enforced decisions ``` ### `ConflictRecord` ```python from cleveragents.actor.reconciliation import ConflictRecord @dataclass(frozen=True) class ConflictRecord: key: str # normalised invariant text used for grouping winner: Invariant # invariant that prevailed losers: list[Invariant] # invariants that were overridden reason: str # human-readable explanation of the resolution ``` ### `ScopeInvariants` Convenience container grouping invariants by scope tier: ```python from cleveragents.actor.reconciliation import ScopeInvariants scope_invs = ScopeInvariants( global_invariants=[...], project_invariants=[...], action_invariants=[...], plan_invariants=[...], ) all_invs = scope_invs.all_invariants() # flat list across all scopes ``` ### `reconcile_invariants` (standalone function) ```python from cleveragents.actor.reconciliation import reconcile_invariants reconciled, conflicts = reconcile_invariants(scope_invariants) # reconciled — list[Invariant] (winners only) # conflicts — list[ConflictRecord] ``` ### Constructing the actor via dependency injection `InvariantReconciliationActor` is instantiated directly with application services resolved from the DI container: ```python from cleveragents.application.container import Container from cleveragents.actor.reconciliation import InvariantReconciliationActor container = Container() actor = InvariantReconciliationActor( invariant_service=container.invariant_service(), decision_service=container.decision_service(), ) ``` ### Failure Behaviour Reconciliation failures block the phase transition with `ReconciliationBlockedError` and emit an `INVARIANT_VIOLATED` event. The exception is raised by `PlanLifecycleService`, importable via `from cleveragents.application.services.plan_lifecycle_service import ReconciliationBlockedError`. Post-correction reconciliation runs via `CORRECTION_APPLIED` event subscription (best-effort; does not block correction completion). --- ## Example: Loading and Compiling an Actor ```python from pathlib import Path from cleveragents.actor import ActorLoader, compile_actor from cleveragents.tool import ToolRegistry loader = ActorLoader() config = loader.load(Path("examples/actors/graph_workflow.yaml")) tool_registry = ToolRegistry() compiled = compile_actor(config, tool_registry) ```