Files
HAL9000 988a169831
CI / benchmark-regression (push) Has been skipped
CI / benchmark-publish (push) Failing after 41s
CI / quality (push) Failing after 1m4s
CI / security (push) Failing after 1m4s
CI / unit_tests (push) Failing after 1m3s
CI / build (push) Failing after 45s
CI / push-validation (push) Successful in 47s
CI / helm (push) Successful in 42s
CI / lint (push) Successful in 1m33s
CI / e2e_tests (push) Failing after 45s
CI / integration_tests (push) Failing after 47s
CI / typecheck (push) Successful in 1m43s
CI / docker (push) Has been skipped
CI / coverage (push) Has been skipped
CI / status-check (push) Failing after 6s
CI / benchmark-publish (pull_request) Has been skipped
CI / helm (pull_request) Successful in 50s
CI / build (pull_request) Successful in 57s
CI / lint (pull_request) Successful in 1m9s
CI / quality (pull_request) Successful in 1m18s
CI / benchmark-regression (pull_request) Failing after 1m16s
CI / typecheck (pull_request) Successful in 1m30s
CI / security (pull_request) Successful in 1m36s
CI / e2e_tests (pull_request) Successful in 3m52s
CI / integration_tests (pull_request) Successful in 4m30s
CI / push-validation (pull_request) Successful in 20s
CI / unit_tests (pull_request) Successful in 6m31s
CI / docker (pull_request) Successful in 1m36s
CI / coverage (pull_request) Successful in 12m19s
CI / status-check (pull_request) Successful in 4s
docs: add InvariantReconciliationActor API docs, devcontainer discovery module guide, and mkdocs nav
- docs/api/actor.md: Add InvariantReconciliationActor section documenting
  the built-in reconciliation actor introduced in v3.8.0, including the
  reconciliation algorithm, ReconciliationResult/ConflictRecord/ScopeInvariants
  data classes, standalone reconcile_invariants() function, DI registration,
  and failure behaviour.

- docs/modules/devcontainer-discovery.md: New module guide for the
  devcontainer auto-discovery system (v3.8.0 fix #2615), covering
  DevcontainerDiscoveryResult, discover_devcontainers(), is_trigger_type(),
  monorepo named-config support, and gotchas.

- mkdocs.yml: Add 'Devcontainer Auto-Discovery' to the Modules nav section,
  alongside shell-safety, uko-provenance, invariant-reconciliation,
  context-hydration, and git-worktree-sandbox module docs.

- robot/coverage_threshold.robot: Add tdd_issue and tdd_issue_4305 tags
  to the Noxfile Contains Coverage Threshold Constant test case.

ISSUES CLOSED: #4485
2026-05-05 06:15:57 +00:00

7.8 KiB

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 and ADR-031 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

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

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

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

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 for the design rationale behind invariant reconciliation.

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

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

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:

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)

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:

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

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)