Files
cleveragents-core/docs/specification/architecture.md

292 KiB
Raw Permalink Blame History

Architecture

This section provides the complete architectural blueprint for CleverAgents. It defines the system's structural composition, technology choices, layer boundaries, data flow, and operational characteristics. An implementor should be able to build the entire system from this section combined with the behavioral specification above.

Architecture Overview

!!! adr "Architecture Decision" The layered architecture, DDD boundaries, and deployment modes are defined in ADR-001: Layered Architecture.

CleverAgents is architected as a layered, event-driven application following Domain-Driven Design (DDD) principles with clean architecture boundaries. The system is organized into four primary layers — Presentation, Application, Domain, and Infrastructure — with strict dependency rules: outer layers depend on inner layers, never the reverse. Cross-cutting concerns (logging, configuration, security) are handled through dependency injection and aspect-oriented patterns.

The architecture supports two deployment modes:

  1. Local Mode: A single-process CLI application where all components run in the same Python process. The CLI is the primary entry point, the database is SQLite, and all resources are local. A2A flows over stdio (agent as subprocess).
  2. Server Mode: A multi-user service where the CLI acts as a thin client communicating with a remote CleverAgents server via A2A over HTTP (JSON-RPC 2.0). The server hosts shared storage (PostgreSQL), namespace resolution, and remote plan execution (via LangGraph Platform RemoteGraph).

Both modes share the same domain model and application layer. The infrastructure layer provides swappable implementations for each deployment target. All communication uses the Agent-to-Agent Protocol (A2A) standard — there is no REST API.

High-Level Component Diagram

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 12
skinparam packageStyle frame
skinparam packageFontSize 14
skinparam packageFontStyle bold
skinparam componentFontSize 11

package "Presentation Layer" <<Frame>> #E0F7FA {
  component [CLI\n(Typer)] as CLI
  component [TUI\n(Textual)] as TUI
  component [Web\n(Textual Web)] as Web
  component [IDE Plugin\n(Embedded TUI)] as IDE
  component [A2A Server\n(JSON-RPC 2.0 Endpoint)] as A2ASRV
}

interface "A2A\n(Agent-to-Agent Protocol)" as A2A #FFD54F
note right of A2A : Local: A2A over stdio\nServer: A2A over HTTP\nJSON-RPC 2.0 wire format

package "Application Layer" <<Frame>> #E3F2FD {
  package "Service Facade" {
    component [PlanService] as PS
    component [ProjectService] as ProjS
    component [ActorService] as ActS
    component [ContextService] as CtxS
    component [ToolService] as ToolS
    component [SkillService] as SkillS
    component [ResourceSvc] as ResS
  }
  package "Workflow Engine" {
    component [PlanLifecycle] as PL
    component [SessionWorkflow] as SW
    component [CorrectionFlow] as CF
    component [MergeWorkflow] as MW
  }
  package "Event Bus" {
    component [StructuredLog] as SL
    component [EventEmitter] as EE
    component [MetricsCollector] as MC
  }
  component [DI Container\n(dependency-injector DeclarativeContainer)] as DI #D1C4E9
}

package "Domain Layer" <<Frame>> #F3E5F5 {
  package "Domain Models" {
    component [Plan / Decision / Project\nResource / Actor / Action\nTool / Skill / Session\nInvariant / AutomationProf\nCheckpoint / CorrectionAtmpt] as DM
  }
  package "Domain Services" {
    component [InvariantEnforcer\nAutonomyCtrl\nContextBuilder\nMergeResolver] as DS
    note bottom of DS : Repository Interfaces\n(Protocol classes)
  }
  package "Domain Events" {
    component [PlanCreated / PhaseChanged\nDecisionMade / ToolInvoked\nApplyCompleted / CorrectionReq] as DE
  }
}

package "Infrastructure Layer" <<Frame>> #FFEBEE {
  package "Database" {
    component [SQLite / SQLAlchemy\nAlembic / UoW] as DB
  }
  package "Indexing" {
    component [Tantivy / FAISS\nNeo4j / Qdrant / RDFLib] as IDX
  }
  package "Sandbox" {
    component [GitWorktree / FsCopy\nTxnRollback / NoOp] as SBX
  }
  package "LLM / AI Runtime" {
    component [LangChain / LangGraph\nProviderRegistry\nRxPY Bridge / MCP SDK] as LLM
  }
  package "External Integrations" {
    component [MCP Servers\nAgent Skills Std\nREST/gRPC Clients] as EXT
  }
  package "LSP Runtime" {
    component [LSP Server Manager\nLSPToolAdapter\nLanguage Servers] as LSPR
  }
  package "File System / OS" {
    component [Watchdog / File I/O\nGit CLI / Subprocess\nOS Process / Signals] as FS
  }
}

CLI -[hidden]right-> TUI
TUI -[hidden]right-> Web
Web -[hidden]right-> IDE
IDE -[hidden]right-> A2ASRV

CLI -down-> A2A
TUI -down-> A2A
Web -down-> A2A
IDE -down-> A2A
A2ASRV -down-> A2A

A2A -down-> PS
A2A -down-> SW
A2A -down-> EE

PS -down-> DI
DI -down-> DM
DS -down-> DB
DS -down-> IDX
@enduml

Standards and Protocols in the Architecture

!!! adr "Architecture Decision" The four protocol standards and their architectural roles are defined across ADR-026: Agent-to-Agent Protocol (A2A), ADR-027: Language Server Protocol (LSP) Integration, ADR-028: Agent Skills Standard (AgentSkills.io), and ADR-029: Model Context Protocol (MCP) Adoption.

Each adopted standard maps to an explicit architectural boundary:

  • A2A (Agent-to-Agent Protocol) — the A2A standard (JSON-RPC 2.0) connects Presentation-layer clients (CLI, TUI, IDE) to the Application layer. Standard A2A operations handle agent messaging (message/send, message/stream), task lifecycle, and streaming updates. CleverAgents _cleveragents/-prefixed extension methods handle platform operations (plans, registries, context, sync). In local mode A2A flows over stdio; in server mode over HTTP. This keeps clients interchangeable and supports third-party client and agent development.
  • LSP (Language Server Protocol) — LSP servers are registered in the LSP Registry (Infrastructure layer) and bound to actor graph nodes to provide language intelligence. The LSPToolAdapter exposes LSP capabilities as tools (diagnostics, hover, go-to-definition, completions, references, rename) that actors call during reasoning, and the LSP Runtime automatically enriches actor context with diagnostic and type information. This gives actors the same semantic code understanding that human developers get from IDEs. See LSP Integration.
  • MCP (Model Context Protocol) — external MCP servers reside in the Infrastructure layer. The MCPToolAdapter bridges them into first-class Tools in the Domain model with extended capability metadata. MCP tools are composed into skills and used as tool nodes in actor graphs, giving the system standardized tool discovery and invocation.
  • Agent Skills (https://AgentSkills.io) — Agent Skills are ingested into the Skill Registry and loaded by the actor runtime via progressive disclosure. Inside actor graphs they appear as tool nodes whose execution is instruction-driven — the agent follows multi-step procedures that may call MCP tools, built-ins, or other Agent Skills. This adds portable, reusable workflows that complement schema-driven MCP tools.

A2A Integration Architecture

!!! adr "Architecture Decision" The Agent-to-Agent Protocol is defined in ADR-026: Agent-to-Agent Protocol (A2A). The adoption of the A2A standard is defined in ADR-047: A2A Standard Adoption. See also Core Concepts > Server > A2A for the behavioral specification.

Of the four standards CleverAgents adopts, A2A has the deepest architectural integration. While MCP, Agent Skills, and LSP plug into the Infrastructure and Domain layers to provide capabilities to actors, A2A defines the fundamental boundary between the Presentation and Application layers — it is the protocol surface through which every client operation flows, in every deployment mode. This section provides the full architectural detail.

Why A2A Exists

The architecture faces a structural tension: the system has multiple Presentation-layer surfaces (CLI, TUI, Web, IDE plugin) and multiple deployment modes (local, server). Without a shared contract:

  1. Each client would implement its own calling convention into the Application layer.
  2. Local and server modes would develop behavioral drift.
  3. Third-party client development would be impractical.
  4. Testing would require N x M test matrices (N clients x M modes).

CleverAgents adopts the Agent-to-Agent Protocol standard (a2a-protocol.org), built on JSON-RPC 2.0, as the sole protocol for all client-server communication. The contract is the same whether the transport is stdio (local) or HTTP (server). This is the architectural guarantee that clients are interchangeable.

A2A in the Layer Diagram

A2A sits at the boundary between the Presentation and Application layers. Every arrow from a Presentation-layer component to the Service Facade in the High-Level Component Diagram represents an A2A method call:

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 11

package "Presentation Layer" #E0F7FA {
  component [CLI] as CLI
  component [TUI] as TUI
  component [Web] as Web
  component [IDE Plugin\n(Embedded TUI)] as IDE
  component [A2A Server Endpoint\n(JSON-RPC 2.0)] as A2ASRV
}

interface "A2A" as A2A_IF #FFD54F

package "Application Layer" #E3F2FD {
  component [Service Facade\n(PlanService, ProjectService,\nActorService, ContextService,\nToolService, SkillService,\nResourceService)] as SF
  component [SessionWorkflow] as SW
  component [EventEmitter] as EE
}

CLI -down-> A2A_IF
TUI -down-> A2A_IF
Web -down-> A2A_IF
IDE -down-> A2A_IF
A2ASRV -down-> A2A_IF

A2A_IF -down-> SF
A2A_IF -down-> SW
A2A_IF -down-> EE

note right of A2A_IF
  **Local mode**: A2A over stdio
  **Server mode**: A2A over HTTP
  JSON-RPC 2.0 wire format
end note
@enduml
Transport Modes

The transport mechanism is the core architectural enabler that makes A2A work across deployment modes. The A2A Python SDK provides both transports:

Local Mode — A2A over stdio

CLI  ──stdio──→  Agent Subprocess  ──→  A2aLocalFacade  ──→  ServiceFacade.method()  ──→  Domain / Infrastructure

The client spawns the agent as a subprocess. A2A JSON-RPC messages flow over stdin/stdout. Standard A2A operations (message/send, message/stream) drive the conversation. Extension methods (_cleveragents/*) are intercepted by A2aLocalFacade and routed to in-process Application-layer services. No network, no authentication — the agent runs with local user permissions.

Server Mode — A2A over HTTP

CLI  ──HTTP──→  CleverAgents A2A Server  ──→  ServiceFacade.method()  ──→  Domain / Infrastructure
                     │                                                        │
                     └── RemoteGraph.invoke() ──→  LangGraph Platform ────────┘

The client connects to the CleverAgents server via the A2A SDK's HTTP transport. All methods — both standard A2A and _cleveragents/ extensions — flow through the single A2A JSON-RPC 2.0 endpoint. The server delegates actor execution to LangGraph Platform via RemoteGraph. Authentication is required via HTTP auth schemes declared in the Agent Card.

External Agent Mode — Split Routing

CLI  ──HTTP──→  External A2A Server  (standard A2A operations: message/send, message/stream, etc.)
     ──HTTP──→  CleverAgents Server  (_cleveragents/* extension methods only)

When an agent is hosted on an external A2A-compatible server, the client routes standard A2A operations to that server and _cleveragents/ extension methods to the CleverAgents server. This enables interoperability with any A2A-compliant agent.

This transport architecture is enforced by:

  • The same A2A test suite runs in both modes. Contract tests validate that a given method with given parameters produces the same response shape over stdio and HTTP.
  • No Presentation-layer module imports from Infrastructure. Architecture tests (import-linter) verify this on every commit, preventing clients from bypassing A2A.
  • The Service Facade is the sole entry point. No Application-layer service is exposed directly to the Presentation layer.
Complete Method Routing

Every A2A method routes to a specific Application-layer service method. The following tables are the authoritative mapping.

Standard A2A Operations → Service Mapping

A2A Operation Service Method
initialize Capability negotiation — returns supported extensions, automation profiles, tool registries
authenticate Token validation against user/token store
session/new SessionWorkflow.create()
session/load SessionWorkflow.resume()
session/list SessionWorkflow.list()
session/prompt SessionWorkflow.tell()
session/cancel PlanService.cancel() (cancels in-progress plan execution)
session/set_mode AutomationProfileService.switch()
session/set_model ProviderRegistry.switch_model()
session/fork SessionWorkflow.fork()
session/resume SessionWorkflow.resume()

Plan Extension Methods → Service Mapping

A2A Extension Method Service Method
_cleveragents/plan/use PlanService.create_plan()
_cleveragents/plan/execute PlanLifecycle.execute()
_cleveragents/plan/apply PlanLifecycle.apply()
_cleveragents/plan/cancel PlanService.cancel()
_cleveragents/plan/status PlanService.get_status()
_cleveragents/plan/tree PlanService.get_tree()
_cleveragents/plan/explain PlanService.explain_decision()
_cleveragents/plan/correct CorrectionFlow.correct()
_cleveragents/plan/diff PlanService.get_diff()
_cleveragents/plan/artifacts PlanService.get_artifacts()
_cleveragents/plan/prompt PlanService.inject_guidance()
_cleveragents/plan/rollback PlanLifecycle.rollback()
_cleveragents/plan/list PlanService.list()

Registry Extension Methods → Service Mapping (pattern applies to all entity types)

A2A Extension Method Pattern Service Method Pattern
_cleveragents/registry/{entity}/list {Entity}Service.list()
_cleveragents/registry/{entity}/show {Entity}Service.show()
_cleveragents/registry/{entity}/add {Entity}Service.add()
_cleveragents/registry/{entity}/update {Entity}Service.update()
_cleveragents/registry/{entity}/remove {Entity}Service.remove()

Where {entity} is one of: actor, skill, tool, validation, resource, resource_type, project, action, automation_profile, invariant, lsp.

Context Extension Methods → Service Mapping

A2A Extension Method Service Method
_cleveragents/context/show ContextService.show()
_cleveragents/context/inspect ContextService.inspect()
_cleveragents/context/simulate ContextService.simulate()
_cleveragents/context/set ContextService.set()

Sync, Namespace, and Health Extension Methods

A2A Extension Method Service Method
_cleveragents/sync/pull SyncService.pull()
_cleveragents/sync/push SyncService.push()
_cleveragents/sync/status SyncService.status()
_cleveragents/namespace/list NamespaceService.list()
_cleveragents/namespace/show NamespaceService.show()
_cleveragents/namespace/members NamespaceService.members()
_cleveragents/health/check Health check handler
_cleveragents/diagnostics/run Diagnostic runner
Streaming Architecture

A2A streaming uses Server-Sent Events (SSE) via message/stream. The server pushes TaskStatusUpdateEvent and TaskArtifactUpdateEvent to the client in real-time during long-running operations:

Event types and their CleverAgents mappings:

Event Type Emitted When Payload
TaskStatusUpdateEvent Agent produces response tokens, task state changes, automation profile switched status (state + message with agent response Parts)
TaskArtifactUpdateEvent Tool execution results, plan artifacts generated artifact (named artifact with Parts)

Example streaming event:

{
  "jsonrpc": "2.0",
  "method": "task/statusUpdate",
  "params": {
    "taskId": "task_01HXM8C2...",
    "status": { "state": "working" },
    "message": { "role": "agent", "parts": [{ "kind": "text", "text": "Running git-diff on platform-repo..." }] }
  }
}

Causal Ordering Guarantee: Events within a single session/plan lifecycle are delivered in causal order. A phase transition event always arrives after the preceding phase completion.

In local mode (stdio), events flow as JSON-RPC messages over stdout. In server mode (HTTP), the A2A SDK manages the SSE streaming connection. Both modes deliver the same event types with the same payload shapes.

Authentication and Authorization

Authentication follows the A2A standard Agent Card-based discovery:

  1. Agent Card discovery: Client fetches the server's Agent Card (via /.well-known/agent.json or configured URL). The Agent Card declares supported capabilities, _cleveragents extensions, and authentication schemes.
  2. HTTP authentication: Client authenticates using the scheme declared in the Agent Card (OAuth2, API key, Bearer token). Typically Authorization: Bearer <server.token> header on every request.
  3. All subsequent operations: Authenticated. The A2A-Version header is sent on every request.

Local mode (stdio): Authentication is bypassed entirely. The agent subprocess runs with the user's local permissions.

Server mode: Every connection must discover the Agent Card and authenticate before any operation. Unauthenticated requests receive a JSON-RPC error:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": { "code": -32002, "message": "Authentication required" }
}

Authorization is namespace-scoped: users can only access entities within namespaces they have been granted access to. The local/ namespace is never accessible via server.

Error Taxonomy

A2A uses JSON-RPC 2.0 integer error codes. Standard protocol errors use the reserved range; application-specific errors use -32001 to -32099:

Error Code Meaning Domain Exception(s)
-32700 Parse error (malformed JSON)
-32600 Invalid request
-32601 Method not found A2aOperationNotFoundError
-32602 Invalid params ValidationError
-32603 Internal error Any unhandled Exception
-32001 Entity not found ResourceNotFoundError
-32002 Authentication required AuthenticationError
-32003 Authorization forbidden AuthorizationError
-32004 Invalid state BusinessRuleViolation
-32005 Already exists DuplicateEntityError
-32006 Budget exceeded BudgetExceededError
-32007 Version mismatch A2aVersionMismatchError
-32008 Plan error PlanError

All errors follow the JSON-RPC 2.0 error structure: { "code": <int>, "message": <string>, "data": <optional object> }. Clients should dispatch on code for precise error handling.

A2A Versioning
  • The JSON-RPC protocol version is always "2.0" (in the jsonrpc field)
  • A2A protocol version is declared in the Agent Card and sent via the A2A-Version HTTP header
  • CleverAgents extension version is declared in the Agent Card's extensions section under _cleveragents.version
  • Backward-compatible additions (new optional params, new extension methods) are permitted within a major version; breaking changes require a major version bump
  • Servers support the current extension version plus one prior minor version
A2A and the IDE Plugin

The IDE plugin is an embedded TUI — essentially the same Textual-based interface used by the standalone TUI, but hosted inside an IDE environment. Like all Presentation-layer clients, it communicates with the backend exclusively through A2A. It does not access the Domain or Infrastructure layers directly — all plan operations, validation queries, and context lookups go through A2A. This means:

  • The IDE plugin works identically in local mode (A2A over stdio to a subprocess) and server mode (A2A over HTTP).
  • IDE features stay exactly aligned with CLI/TUI behavior (same A2A operations, same JSON-RPC 2.0 format).
  • The IDE plugin receives TaskStatusUpdateEvent / TaskArtifactUpdateEvent streaming events for real-time plan and session updates.
  • Extension methods (_cleveragents/fs/*, _cleveragents/terminal/*) allow the agent to interact with the IDE's workspace and integrated terminal.

Note that the Language Server Protocol (LSP) in CleverAgents is unrelated to the IDE plugin. LSP serves an entirely different purpose: providing language intelligence to actors and agents in the Infrastructure layer. See LSP Integration and ADR-027: Language Server Protocol (LSP) Integration for the full LSP architecture.

Server and Client Architecture

!!! adr "Architecture Decision" The server application architecture is defined in ADR-048: Server Application Architecture. The A2A standard adoption is defined in ADR-047: A2A Standard Adoption.

This section defines the complete architecture of the CleverAgents server application and the client-side components that interact with it. The server is a separate application (distinct deployment unit) that shares the same Domain and Application layers as the client.

Server Application Structure

The server follows the same four-layer architecture as the client (ADR-001), sharing two layers and providing its own implementations for the other two:

Layer Client Server Shared?
Domain Business rules, domain models, domain events Same Yes — identical package
Application Service facades, workflows, event bus Same Yes — identical package
Infrastructure SQLite, local sandbox, local file access, A2aLocalFacade PostgreSQL, LangGraph Platform RemoteGraph, server-side sandbox, A2A SDK server No
Presentation CLI (Typer), TUI (Textual), IDE plugin A2A JSON-RPC 2.0 endpoint No

The shared Domain and Application layers are consumed as a Python package dependency. This ensures zero behavioral drift between local and server modes.

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 11

package "Client Application" #E0F7FA {
  component [CLI / TUI / IDE Plugin] as ClientPres
  component [Application Layer\n(shared)] as ClientApp
  component [Domain Layer\n(shared)] as ClientDom
  component [Infrastructure\n(SQLite, local sandbox,\nA2aLocalFacade)] as ClientInfra
}

package "Server Application" #FFEBEE {
  component [A2A JSON-RPC 2.0\nEndpoint] as ServerPres
  component [Application Layer\n(shared)] as ServerApp
  component [Domain Layer\n(shared)] as ServerDom
  component [Infrastructure\n(PostgreSQL, RemoteGraph,\nA2A SDK server)] as ServerInfra
}

component [LangGraph\nPlatform] as LGP #FFF9C4

ClientPres -down-> ClientApp
ClientApp -down-> ClientDom
ClientDom -down-> ClientInfra

ServerPres -down-> ServerApp
ServerApp -down-> ServerDom
ServerDom -down-> ServerInfra

ServerInfra -right-> LGP : RemoteGraph

ClientPres -right[hidden]-> ServerPres

note bottom of ClientApp : Same Python package
note bottom of ServerApp : Same Python package
@enduml
Server Presentation Layer

The server's sole client-facing interface is an A2A JSON-RPC 2.0 endpoint implemented using the A2A Python SDK's server-side connection handler. There is no REST API, no GraphQL, no separate admin endpoint.

The endpoint handles three categories of messages:

  1. Standard A2A operations (message/send, message/stream): Routed to SessionWorkflow and actor execution (via LangGraph Platform RemoteGraph); Agent Card served at discovery endpoint
  2. Extension methods (_cleveragents/*): Routed to Application-layer services (PlanService, RegistryServices, SyncService, NamespaceService, etc.)
  3. Multi-turn interactions to clients (Task input-required state, _cleveragents/fs/*, _cleveragents/terminal/*): Forwarded from executing actors back to the connected client — enabling server-hosted agents to access client-local resources
Server Infrastructure: LangGraph Platform

Actor graphs (StateGraphs defined in YAML) are deployed to LangGraph Platform as separate deployments. The server invokes them via RemoteGraph:

sequenceDiagram
    participant C as Client
    participant S as A2A Server
    participant SW as SessionWorkflow
    participant RG as RemoteGraph
    participant LG as LangGraph Platform

    C->>S: message/send {"message": {"role": "user", "parts": [{"text": "Add pagination"}]}}
    S->>SW: tell("Add pagination")
    SW->>RG: invoke(strategy_actor, state)
    RG->>LG: Execute strategy actor graph
    LG-->>RG: Strategy result (plan entries)
    RG-->>SW: Plan with decisions
    SW-->>S: TaskStatusUpdateEvent (plan notification)
    S-->>C: TaskStatusUpdateEvent (streaming)
    SW->>RG: invoke(execution_actor, state)
    RG->>LG: Execute execution actor graph
    LG-->>RG: Execution result
    RG-->>SW: Completed execution
    SW-->>S: TaskArtifactUpdateEvent (tool_call results)
    S-->>C: TaskArtifactUpdateEvent (streaming)

Different actors for different plan phases (strategy, execution, estimation) each deploy as separate RemoteGraphs, enabling independent scaling without limiting plan capabilities. The SessionWorkflow orchestrates them the same way it orchestrates local actor graphs, but through the RemoteGraph interface.

Server Infrastructure: Persistence
Component Technology Notes
Database PostgreSQL via SQLAlchemy Same ORM as client (SQLite), different dialect
Migrations Alembic Schema migrations for server-specific tables (users, tokens, namespace ACLs)
Session store PostgreSQL Multi-user session persistence with namespace isolation
Plan store PostgreSQL Plan lifecycle records, decision trees, artifacts

Server-specific tables (authentication, namespace ACLs, user management) are additive — they extend the shared schema without modifying it.

Server Security Architecture

Authentication: Uses HTTP auth schemes declared in the server's Agent Card (OAuth2, API key, Bearer token). Tokens are validated against the server's user/token store. HTTP transport carries Authorization: Bearer <token> header on every request.

Authorization: Namespace-scoped. Users can only access entities in namespaces they have been granted access to. The local/ namespace is never accessible via server.

Transport security: HTTPS required for all production deployments (TLS termination at Kubernetes ingress).

Client-Side Architecture

The client uses an A2aClient that wraps the A2A Python SDK and routes method calls through a TransportSelector:

# Conceptual client architecture (not literal implementation)
class TransportSelector:
    """Selects transport based on configuration."""

    def get_transport(self) -> A2aTransport:
        if config.server_url:
            return A2aRemoteTransport(url=config.server_url, token=config.server_token)
        else:
            return A2aStdioTransport(agent_command=["cleveragents", "agent", "serve"])

class A2aClient:
    """Unified client for all A2A communication."""

    def __init__(self, transport: A2aTransport):
        self.connection = A2AClient(transport)

    async def send_message(self, task_id: str, message: str) -> AsyncIterator[Event]:
        """Send a message and stream task events."""
        return await self.connection.send_message(Message(
            role="user",
            parts=[TextPart(text=message)],
        ), task_id=task_id)

    async def plan_status(self, plan_id: str) -> dict:
        """Get plan status via extension method."""
        return await self.connection.request("_cleveragents/plan/status", {
            "plan_id": plan_id,
        })

Evolution of existing source code:

Current Component Becomes Notes
A2aLocalFacade Retained Handles _cleveragents/ dispatch to in-process services; adapts A2A message/send to local workflows
A2aClient Wraps A2A SDK A2AClient SDK provides production transport for both stdio and HTTP
A2aEvent Mapped to TaskStatusUpdateEvent / TaskArtifactUpdateEvent Event types map to A2A streaming event types
A2aEventQueue Retained for local mode Delivers events as in-process streaming events
Local/Server Interchangeability

The transport selector pattern ensures that application code is transport-agnostic. A CLI command works identically regardless of whether the backend is local or remote:

# CLI command — identical in local and server mode
async def plan_status_command(plan_id: str):
    client = get_a2a_client()  # TransportSelector picks stdio or HTTP
    result = await client.plan_status(plan_id)
    render_plan_status(result)

The only difference is where computation happens:

Aspect Local Mode Server Mode
Transport A2A over stdio A2A over HTTP
Actor execution In-process LangGraph LangGraph Platform RemoteGraph
Database SQLite PostgreSQL
Authentication Bypassed HTTP auth (Agent Card scheme) + Bearer token
File/terminal access Direct Via A2A multi-turn interactions (server → client)
Namespace resolution local/ only local/ + server namespaces
Server Deployment
Component Technology Notes
Container Docker Server packaged as container image
Orchestration Kubernetes Production deployment target
Configuration Helm chart (k8s/) Configurable replicas, resource limits, ingress
Ingress Kubernetes Ingress TLS termination, routing to A2A endpoint
Database PostgreSQL (managed or self-hosted) External to the application container
LangGraph Platform Managed or self-hosted External service for graph execution
Caching Redis (optional) Multi-instance session affinity, rate limiting

Architectural Principles

  1. Hexagonal Architecture (Ports and Adapters): The domain layer defines abstract repository interfaces (ports). The infrastructure layer provides concrete implementations (adapters). This allows swapping SQLite for PostgreSQL, FAISS for Qdrant, or local execution for remote server execution without touching domain logic.

  2. Command Query Responsibility Segregation (CQRS): Write operations (plan creation, decision recording, resource modification) flow through command handlers that enforce invariants and emit domain events. Read operations (plan status, decision tree visualization, context queries) use optimized read paths that may bypass the ORM for performance.

  3. Event-Driven Architecture: All significant state changes emit domain events through a structured event bus. Events flow through RxPY reactive streams for real-time processing and through the structured logging pipeline for persistence. This enables decoupled observability, audit logging, and future webhook/notification systems.

  4. Dependency Injection: All service dependencies are wired through a DeclarativeContainer (from the dependency-injector library). This eliminates hidden coupling, enables testing with mock implementations, and supports runtime reconfiguration (e.g., switching providers).

  5. Convention over Configuration: Default behavior requires zero configuration. For entity registration commands, YAML configuration files are the complete definition (no CLI overrides). For runtime commands (e.g., plan use), CLI flags may override entity defaults. Environment variables can be interpolated into configuration files via ${ENV_VAR} syntax. The resolution chain depends on context: entity registration uses config-file-only semantics, while runtime commands support per-invocation CLI overrides of entity defaults.

Data Flow: Plan Lifecycle

The following diagram traces data flow through the system during a complete plan lifecycle:

sequenceDiagram
    participant U as User
    participant C as CLI
    participant A as Application
    participant D as Domain
    participant I as Infrastructure

    U->>C: plan use
    C->>A: PlanService.create_plan()
    A->>D: Plan(phase=strategize)
    D->>I: PlanRepository.save(plan)

    A->>D: InvariantEnforcer.reconcile()
    A->>D: AutonomyCtrl.should_proceed

    rect rgb(200, 230, 255)
        Note over A,D: Strategize Phase (if auto)
        A->>D: StrategyActor.invoke()
        D->>I: LangGraph StateGraph.invoke
        D->>I: ContextBuilder.build_hot_ctx()
        I-->>D: [FAISS/Tantivy results]
        D->>I: LLM Provider.generate()
        I-->>D: [LLM response]
        D-->>A: Decision[]
        D->>I: DecisionRepo.save_batch()
    end

    rect rgb(255, 230, 200)
        Note over A,D: Execute Phase
        A->>D: SandboxManager.create()
        D->>I: GitWorktree.create_worktree()
        A->>D: ExecutionActor.invoke(sandbox)
        D->>I: Tool calls via SkillRegistry
        D-->>A: ChangeSet
    end

    rect rgb(200, 255, 200)
        Note over A,D: Apply Phase
        A->>D: Validation.run_all()
        D->>I: Subprocess.run(commands)
        A->>D: SandboxManager.commit()
        D->>I: git merge
    end

    C-->>U: OK Applied

Technical Stack

!!! adr "Architecture Decision" The technology selections, version constraints, and rationale are defined in ADR-005: Technical Stack.

This section enumerates every technology choice in the CleverAgents stack, organized by functional area. Version constraints are minimum versions; newer compatible versions are acceptable.

Core Runtime

Technology Version Role Rationale
Python >= 3.13 Primary language Type hint maturity, async/await, pattern matching, performance improvements in 3.13. The entire codebase is Python-only for consistency and LLM ecosystem alignment.
Hatchling >= 1.21.0 PEP 517 build backend Lightweight, standards-compliant build system. Produces wheels and sdists without complex configuration.
uv >= 0.8.0 Package installer and resolver 10-100x faster than pip. Used in CI, Docker builds, and Nox sessions for dependency installation. Drop-in replacement for pip with full PEP 723 support.

CLI and Presentation

Technology Version Role Rationale
Typer >= 0.9.0 CLI framework Built on Click with automatic help generation, type inference from Python type hints, and Rich integration for styled output. Supports nested command groups matching the agents <noun> <verb> pattern.
Rich (transitive via Typer) Terminal rendering Provides Panel, Table, Tree, Syntax, Progress, Spinner, and Markdown rendering used by all output formats except plain and structured (json/yaml).
Textual (future) TUI framework Built by the Rich maintainers. Provides a reactive terminal UI framework that can be served as a web app via Textual Web, enabling the "single UI codebase" strategy for TUI, Web, and IDE plugin.

LLM and AI Runtime

Technology Version Role Rationale
LangChain >= 0.2.14 LLM abstraction layer Provider-agnostic interface for chat models, embeddings, output parsing, prompt templates, and tool calling. The BaseLanguageModel protocol enables swapping providers without changing application code.
LangGraph (transitive) Stateful workflow orchestration StateGraph with conditional edges, checkpointing (MemorySaver), and streaming execution. Used for the plan generation graph (load_context -> analyze -> generate -> validate) and auto-debug graph.
LangChain Provider Packages varies LLM provider integrations langchain-openai (>= 0.2.0), langchain-google-genai (>= 0.2.0), langchain-anthropic, langchain-groq, langchain-together, langchain-cohere. Each provides a ChatModel implementation.
LangChain Community >= 0.2.14 Community integrations FAISS vector store, FakeListLLM/FakeEmbeddings for testing.
RxPY >= 3.2.0 Reactive stream processing Subject, BehaviorSubject, ReplaySubject, and operators (map, filter, flat_map, debounce, throttle, scan) for real-time event routing between actors, stream-to-graph bridging, and backpressure management.
MCP SDK >= 1.4.0 Model Context Protocol Client SDK for communicating with MCP servers. Enables CleverAgents to discover and invoke tools exposed by any MCP-compliant server.

Data and Persistence

Technology Version Role Rationale
SQLite (system) Primary database Zero-configuration, file-based, ACID-compliant. Sufficient for local mode; supports WAL mode for concurrent reads during plan execution. For server mode, the infrastructure layer swaps in PostgreSQL via the same SQLAlchemy interface.
SQLAlchemy (transitive via Alembic) ORM and database abstraction Declarative model mapping, session management, Unit of Work pattern, and dialect abstraction enabling database portability.
Alembic >= 1.13.1 Database migrations Version-controlled schema migrations with upgrade/downgrade support. Migrations are auto-applied on first run and during agents init.
python-ulid >= 2.7.0 ID generation ULID generation for plans, decisions, resources, and correction attempts. ULIDs are lexicographically sortable by creation time, enabling efficient time-range queries without separate timestamp indexes.

Indexing and Code Intelligence

Technology Version Role Rationale
Tantivy (via tantivy-py) configurable Full-text search Rust-based search engine providing sub-millisecond full-text search across project resources. Used for keyword-based code search during context building. Alternative: SQLite FTS5 for zero-dependency installations.
FAISS (faiss-cpu) >= 1.7.4 Vector similarity search Facebook AI Similarity Search for semantic code search. Indexes embedding vectors generated from project files. Supports approximate nearest neighbor search for fast retrieval during context building.
Qdrant configurable Vector search (alternative) External vector database for production deployments requiring horizontal scaling, persistence, and filtering. Connected via qdrant-client.
Neo4j configurable Knowledge graph Graph database for structural code relationships (class hierarchies, call graphs, dependency trees). Enables queries like "find all callers of function X" during Strategize. Alternative: rdflib for in-process graph queries.
rdflib >= 7.1.4 In-process graph store Python RDF library for lightweight structural code analysis without external dependencies. Stores RDF triples representing code relationships.
OpenAI Embeddings configurable Vector generation Default embedding provider (text-embedding-3-small). Alternative providers: Anthropic, local sentence-transformers models via index.embedding.provider config.

Configuration and Validation

Technology Version Role Rationale
Pydantic >= 2.7.0 Data validation and modeling All domain models, configuration objects, and API schemas use Pydantic v2 for runtime validation, JSON Schema generation, and serialization. Provides type-safe data boundaries between layers.
Pydantic Settings >= 2.11.0 Environment configuration BaseSettings subclasses for environment variable loading with the CLEVERAGENTS_ prefix, .env file support, and nested model resolution. Implements the four-tier configuration resolution chain.
Jinja2 >= 3.1.0 Template rendering Used for actor system prompts, YAML configuration interpolation, and prompt template rendering. Supports {{ context.* }} variable expansion with sandboxed execution.
TOML (stdlib tomllib) Configuration file format Global configuration uses TOML for its native support of nested tables, which maps naturally to the dot-separated hierarchical key structure. Python 3.13 includes tomllib in stdlib.
YAML (via PyYAML) Entity configuration format All entity definitions (actors, skills, tools, actions, resource types, automation profiles) use YAML as the canonical human-authored format. JSON is accepted as valid YAML.

Testing

Technology Version Role Rationale
Behave >= 1.2.6 BDD feature tests Gherkin .feature files describe behavioral scenarios in natural language. Step implementations exercise the full application stack. Custom behave-parallel runner enables parallel execution via ProcessPoolExecutor.
Robot Framework >= 7.3.2 Integration tests Keyword-driven integration tests for end-to-end CLI workflows. robotframework-pabot enables parallel suite execution.
pytest >= 8.0.0 Unit tests Standard Python test framework for unit-level testing with fixtures, parametrization, and plugin support.
pytest-asyncio >= 0.23.0 Async test support Enables async def test_* functions for testing async LangGraph workflows and RxPY stream operations.
pytest-cov / coverage >= 7.11.0 Coverage reporting Target: 85% line coverage. HTML and XML reports generated by Nox sessions. Coverage gates enforce minimum thresholds in CI.
Hypothesis >= 6.136.6 Property-based testing Generates randomized test inputs for invariant verification, particularly useful for testing decision tree operations, merge strategies, and context tier boundaries.
ASV (Airspeed Velocity) >= 0.6.5 Performance benchmarks Tracks plan generation latency, CLI startup time, and indexing throughput across commits. Prevents performance regressions.

Code Quality

Technology Version Role Rationale
Ruff >= 0.1.0 Linting and formatting Single tool replacing Black, Flake8, isort, and pyupgrade. Rules: E, F, W, B, UP, I, SIM, RUF. Line length: 88. Double quotes. 4-space indent.
Pyright >= 1.1.350 Static type checking Strict mode type checking for the entire codebase. Catches type errors at development time rather than runtime. Configured via pyrightconfig.json.

Infrastructure and Deployment

Technology Version Role Rationale
Docker multi-stage Containerization Production image uses python:3.13-slim with multi-stage build. Non-root user (appuser, uid 1000). Entrypoint: python -m cleveragents.
Helm (chart) Kubernetes deployment Helm chart in k8s/ directory for server mode deployment. Linted and template-tested in CI.
Nox >= 2025.4.22 Task automation Session-based task runner for lint, test, build, docs, benchmarks, and coverage. Uses uv as the virtual environment backend for fast session creation.
Forgejo CI (workflow) Continuous integration Self-hosted Forgejo instance with GitHub Actions-compatible workflow syntax. Pipeline: lint -> typecheck -> behave (matrix 3.11/3.12/3.13) -> build -> docker -> helm.
devcontainer CLI (optional) Devcontainer management Used for building and managing devcontainer-instance resources from .devcontainer/devcontainer.json configurations. Falls back to direct Docker/Podman CLI when not installed, with reduced feature support (e.g., devcontainer features may not be available). See ADR-043.

Monitoring and Observability

Technology Version Role Rationale
structlog >= 24.4.0 Structured logging JSON-structured log output with context binding (plan_id, decision_id, actor_name, tool_name). Enables log aggregation, filtering, and correlation across complex plan hierarchies.
LangSmith (optional) LLM observability Optional integration for tracing LLM calls, token usage, latency, and cost. Configured via CLEVERAGENTS_LANGSMITH_* environment variables. Syncs to LANGCHAIN_TRACING_V2.

Additional Libraries

Technology Version Role Rationale
dependency-injector >= 4.41.0 DI container DeclarativeContainer with Singleton, Factory, and Configuration providers. Wires all services, repositories, and infrastructure components.
watchdog >= 4.0.0 File system monitoring Watches project resource directories for changes. Triggers automatic re-indexing when index.auto-reindex is enabled.
numpy >= 2.1.0 Numerical computing Vector operations for embedding similarity computation, confidence score aggregation, and distance calculations in the vector store layer.
uvicorn >= 0.30.1 ASGI server HTTP server for server mode A2A endpoint. Hosts the A2A JSON-RPC 2.0 server for remote plan execution, namespace resolution, and multi-user collaboration.

Dependency Injection

!!! adr "Architecture Decision" The DI container design, wiring pattern, and testing strategy are defined in ADR-003: Dependency Injection.

All service dependencies in CleverAgents are wired through a DeclarativeContainer from the dependency-injector library (>= 4.41.0). This container is the single composition root for the entire application. No service instantiates its own dependencies — all are received through constructor injection managed by the container.

Container Structure

The DI container is a DeclarativeContainer subclass that declares all providers:

  • Singleton providers for long-lived services: database engine, repository implementations, event bus, index backends, provider registry.
  • Factory providers for per-request or per-operation objects: sandbox instances, plan execution contexts, LangGraph state graphs.
  • Configuration provider for loading and exposing the global TOML configuration and environment variables.

Wiring Pattern

The wiring pattern enforces the layered architecture (see Architecture Overview) by ensuring that dependencies always flow inward:

  1. The Domain Layer defines repository interfaces as Protocol classes (e.g., PlanRepository, DecisionRepository, ResourceRepository).
  2. The Infrastructure Layer provides concrete implementations (e.g., SQLAlchemyPlanRepository, SQLAlchemyDecisionRepository).
  3. The DI container maps protocols to implementations using Singleton or Factory providers.
  4. Application Layer services declare their dependencies as constructor parameters typed to the domain protocols.
  5. The container auto-wires these dependencies at application startup.

This pattern ensures that domain and application code never imports infrastructure modules directly. Swapping backends (e.g., SQLite → PostgreSQL, FAISS → Qdrant, local execution → remote server) requires only changing the container provider mapping — no application or domain code changes.

Provider Types

Provider Type Use Case Example
Singleton Shared stateful services Database session factory, event bus, index backends
Factory Per-use transient objects Sandbox instances, execution contexts
Configuration External configuration values TOML config keys, environment variables

Testing Support

In test configurations, the container is reconfigured to supply mock or fake implementations:

  • FakeListLLM / FakeEmbeddings from LangChain Community replace real LLM providers.
  • In-memory repositories replace SQLAlchemy-backed repositories.
  • No-op sandbox strategies replace git worktree or filesystem copy strategies.

The container's override() context manager allows swapping providers for individual test cases without affecting the global configuration.

Container Rules

  1. All cross-layer dependencies must flow through the DI container. Direct instantiation of infrastructure components in application or domain code is prohibited.
  2. The container is initialized exactly once at application startup (in the CLI entry point or server bootstrap). It must not be re-initialized during request handling.
  3. Domain Layer code must never import or reference the dependency-injector library. Domain protocols are plain Python Protocol classes with no DI framework annotations.
  4. Every new service or repository must be registered in the container before use. Unregistered dependencies cause explicit startup failures, not silent runtime errors.
  5. A startup self-test verifies that all declared providers can be resolved without errors. This runs as part of the application bootstrap and in CI.

Data Validation

!!! adr "Architecture Decision" The data validation strategy, Pydantic usage, and schema generation approach are defined in ADR-004: Data Validation.

CleverAgents processes data from multiple untrusted or semi-trusted sources: user CLI input, YAML configuration files, LLM-generated tool call arguments, MCP server responses, environment variables, and database records. Every boundary crossing — between layers, between the system and external services, between the user and the application — is an opportunity for invalid data to enter the system and cause subtle downstream failures.

All domain models, configuration objects, and API schemas use Pydantic V2 (>= 2.7.0) for runtime data validation, JSON Schema generation, and serialization. Pydantic Settings (>= 2.11.0) extends this to environment variable loading. Pydantic serves as the type-safe data boundary between all layers.

Domain Models

Every domain entity — Plan, Decision, Action, Resource, Actor, Tool, Skill, Session, Invariant, AutomationProfile, Checkpoint, CorrectionAttempt — is a Pydantic BaseModel subclass. Fields use Python type annotations with Pydantic field validators for business rule enforcement.

Configuration Objects

YAML entity configuration files (actors, skills, tools, actions, resource types, automation profiles, context views) are parsed into Pydantic models. Validation errors are reported with field paths and human-readable messages before any processing occurs. A partially valid configuration must never be applied — YAML files must validate completely before any side effects occur.

Environment and Global Configuration

BaseSettings subclasses from Pydantic Settings handle environment variable loading with the CLEVERAGENTS_ prefix, .env file support, and nested model resolution. This implements the four-tier configuration resolution chain: CLI flag > environment variable > project-scoped config > global config file > built-in default.

JSON Schema Generation

Pydantic models automatically generate JSON Schema definitions, which are used for:

  • Tool input/output schema definitions consumed by LLM tool-calling protocols.
  • Validation of LLM-generated tool call arguments before execution.
  • API request/response schema documentation.

JSON Schema generated from Pydantic models is the canonical schema for tool definitions. Manual schema definitions must not diverge from the model.

Serialization

Pydantic handles serialization to and from JSON, YAML (via dict intermediary), and database column types. Custom serializers are defined for domain-specific types (ULIDs, datetime formats, enum values).

Data Validation Rules

  1. All data crossing a layer boundary must pass through a Pydantic model. Raw dictionaries or untyped data structures must not propagate between layers.
  2. Strict mode is used where appropriate to prevent implicit type coercion (e.g., strings silently becoming integers).
  3. Validation error messages must include the field path and a human-readable explanation suitable for CLI display.
  4. Pyright in strict mode verifies that Pydantic model field types are consistent across the codebase.
  5. Schema drift tests verify that JSON Schema generated from Pydantic tool models matches the schemas expected by MCP and LangChain tool-calling protocols.

ACMS (Advanced Context Management System)

!!! adr "Architecture Decision" The ACMS architecture, UKO layer, CRP protocol, and context strategy framework are defined in ADR-014: Context Management (ACMS).

This section provides the complete architectural design of the Advanced Context Management System (ACMS). For the conceptual overview, core data types, CRP skill definition, strategy protocol, and plan lifecycle integration, see Core Concepts > Advanced Context Management System (ACMS).

Architectural Overview

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 12
skinparam packageFontSize 13
skinparam packageFontStyle bold
skinparam componentFontSize 11

package "Actor / Skill" as actor #E0F7FA {
  component [Issues ContextRequests\nvia Context Request Protocol] as CRP
}

package "Context Assembly Pipeline\n(10-component pluggable)" as pipeline #C8E6C9 {
  package "Phase 1: Strategy Orchestration" {
    component [StrategySelector] as SS
    component [BudgetAllocator] as BA
    component [StrategyExecutor] as SE
  }
  package "Phase 2: Fragment Fusion" {
    component [Deduplicator → DepthResolver\n→ Scorer → Packer → Orderer] as Fusion
  }
  package "Phase 3: Finalization" {
    component [PreambleGenerator\nSkeletonCompressor] as Final
  }
}

package "Strategies" as strats #E8EAF6 {
  component [ARCE\n(graph-aware)] as S1
  component [Simple\n(keyword/embed)] as S2
  component [Breadth/Depth\nNavigator] as S3
  component [Custom\n(user-defined)] as S4
}

package "Backend Abstraction Layer (BAL)" as bal #FFF9C4 {
  component [ScopedView\n(Text DB)] as TV
  component [ScopedView\n(Vector DB)] as VV
  component [ScopedView\n(Graph DB)] as GV
}

package "Physical Data Stores" as stores #E3F2FD {
  component [Tantivy /\nSQLite FTS] as TDB
  component [FAISS /\nQdrant / Weaviate] as VDB
  component [Blazegraph / Jena /\nNeo4j / Stardog\n(UKO triples)] as GDB
}

package "Resource Registry" as rr #F3E5F5 {
  component [Physical/virtual resources\nDAG links] as RR
}

CRP -down-> SS
SS -down-> BA
BA -down-> SE
SE -down-> S1
SE -down-> S2
SE -down-> S3
SE -down-> S4
S1 -down-> TV
S1 -down-> VV
S1 -down-> GV
S2 -down-> TV
S2 -down-> VV
S3 -down-> GV
S4 -down-> TV

SE -right[hidden]-> Fusion
Fusion -right[hidden]-> Final

TV -down-> TDB
VV -down-> VDB
GV -down-> GDB
TDB -down-> RR
VDB -down-> RR
GDB -down-> RR
@enduml

Key Design Principles:

  1. Strategy-agnostic framework: The framework itself has no opinion about how context is assembled. That is the job of strategies. The framework provides the request protocol, the data plumbing, and the Context Assembly Pipeline.
  2. Dynamic budget: The token budget for context assembly may change on every invocation. Strategies receive the current budget and must respect it. The pipeline's BudgetPacker enforces the budget as a hard ceiling.
  3. Hierarchical by default: Every context assembly occurs within the context of a plan hierarchy. The system tracks parent context and provides progressive focusing primitives.
  4. Provenance everywhere: Every piece of context delivered to an actor can be traced back to a specific resource, a specific location within that resource, a specific revision, and a specific UKO node.
  5. Pluggable Architecture: Every component can be extended or replaced.
  6. Progressive Enhancement: System works with basic text search, enhances with advanced features.
  7. Eager Indexing: Indices are built immediately when resources are added and kept continuously up-to-date.
  8. Agent Awareness: Agents understand available indices through skills.
  9. Real-time Synchronization: Indices update immediately as code changes.

Universal Knowledge Ontology (UKO)

Design Philosophy

The UKO is designed around a single principle: everything that an actor might want to know about a resource should be representable as a node in the knowledge graph, at any level of detail, with provenance back to the originating resource.

The ontology uses an inheritance hierarchy so that:

  • Layer 0 defines universal concepts that apply to any information (code, docs, data, infra). DetailDepth is defined here as a non-negative integer with domain-agnostic semantics: depth 0 is the most minimal representation (just a name/identifier), and each increment reveals progressively more structure, content, and relationships. There is no fixed upper bound — the maximum meaningful depth depends on the domain.
  • Layer 1 specializes for domain-specific concepts: general software (uko-code: — modules, callables, types), documents (uko-doc: — sections, paragraphs, citations), data schemas (uko-data: — tables, columns, constraints), and infrastructure (uko-infra: — services, endpoints, config). Each domain registers a DetailLevelMap that assigns named labels to specific integer depths (e.g., uko-code: maps MODULE_LISTING → 0, SIGNATURES → 4, FULL_SOURCE → 9).
  • Layer 2 specializes for paradigm/format-specific concepts within a domain: object-oriented (uko-oo:), functional (uko-func:), procedural (uko-proc:) for code. Future extensions could add academic vs. technical for documents, or relational vs. graph for databases. Each paradigm extends its parent domain's DetailLevelMap, potentially inserting new named levels between existing ones (which shifts subsequent integer assignments).
  • Layer 3 specializes for technology-specific concepts: Python (uko-py:), TypeScript (uko-ts:), Rust (uko-rs:), Java (uko-java:), and potentially Markdown, PostgreSQL, or others. Language/technology-specific DetailLevelMap extensions are added only when they provide meaningful additional semantics beyond the paradigm level.

This layering means a strategy that operates at Layer 0 can work with any resource — code, documents, databases, infrastructure — while a strategy that operates at Layer 3 can provide language-specific or technology-specific intelligence. The breadth-depth-navigator strategy typically operates at Layers 0-1 (universal graph traversal), while arce leverages all layers. DetailDepth resolution follows the DetailLevelMap inheritance chain: when a named level is requested, the system looks up the name in the most specific map available for the target UKO node type (e.g., uko-py: first), then walks up to the parent map (uko-oo:uko-code:uko:) until a match is found. When a raw integer is specified, it is used directly.

Ontology Hierarchy

The UKO defines five namespace prefixes:

Prefix IRI Purpose
uko: https://cleveragents.ai/ontology/uko# Layer 0: Universal foundation
uko-code: https://cleveragents.ai/ontology/uko/code# Layer 1: General software
uko-oo: https://cleveragents.ai/ontology/uko/oo# Layer 2: Object-oriented paradigm
uko-func: https://cleveragents.ai/ontology/uko/func# Layer 2: Functional paradigm
uko-proc: https://cleveragents.ai/ontology/uko/proc# Layer 2: Procedural paradigm
uko-py: https://cleveragents.ai/ontology/uko/py# Layer 3: Python-specific
uko-ts: https://cleveragents.ai/ontology/uko/ts# Layer 3: TypeScript-specific
uko-rs: https://cleveragents.ai/ontology/uko/rs# Layer 3: Rust-specific
uko-java: https://cleveragents.ai/ontology/uko/java# Layer 3: Java-specific
uko-doc: https://cleveragents.ai/ontology/uko/doc# Layer 1: Documents
uko-data: https://cleveragents.ai/ontology/uko/data# Layer 1: Data schemas
uko-infra: https://cleveragents.ai/ontology/uko/infra# Layer 1: Infrastructure
Layer 0: Universal Foundation (uko:)

Every UKO node, regardless of domain, is one of five base classes:

@prefix uko: <https://cleveragents.ai/ontology/uko#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

# ── Base Classes ────────────────────────────────────────────────── uko:InformationUnit a owl:Class ; rdfs:comment "The root of every UKO node. Anything that can appear in an actor's context." .

uko:Container a owl:Class ; rdfs:subClassOf uko:InformationUnit ; rdfs:comment "An information unit that contains other information units (file, module, class, section)." .

uko:Atom a owl:Class ; rdfs:subClassOf uko:InformationUnit ; rdfs:comment "A leaf-level information unit (function body, paragraph, config value)." .

uko:Annotation a owl:Class ; rdfs:subClassOf uko:InformationUnit ; rdfs:comment "Metadata attached to another information unit (comment, docstring, attribute)." .

uko:Boundary a owl:Class ; rdfs:subClassOf uko:InformationUnit ; rdfs:comment "An interface point between containers (export, API endpoint, public method signature)." .

# ── Core Relationships ──────────────────────────────────────────── uko:contains a owl:ObjectProperty ; rdfs:domain uko:Container ; rdfs:range uko:InformationUnit ; rdfs:comment "Parent contains child (e.g., module contains class)." .

uko:references a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Weak reference (e.g., a function mentions a type in a docstring)." .

uko:dependsOn a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Strong dependency (e.g., import, inheritance, call)." .

# ── Content Properties ──────────────────────────────────────────── uko:hasRendering a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "A rendered text representation of this node at a specific detail depth. Multiple renderings at different depths may exist for the same node. The depth is indicated by the associated uko:renderingDepth value." .

uko:renderingDepth a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:nonNegativeInteger ; rdfs:comment "The integer detail depth at which the associated uko:hasRendering was produced. Paired with uko:hasRendering via a blank node or reification." .

uko:hasFullContent a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "Shorthand for the maximum-depth rendering of this node (complete, unabridged content). Equivalent to uko:hasRendering at the domain's maximum depth." .

# ── Provenance ──────────────────────────────────────────────────── uko:sourceResource a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:comment "The CleverAgents Resource (by ULID) this node was extracted from." .

uko:sourcePath a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "Path within the resource (e.g., file path relative to the resource root)." .

uko:sourceRange a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "Byte or line range within the source file (e.g., '42:1-87:0')." .

# ── Temporal ────────────────────────────────────────────────────── uko:validFrom a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:dateTime .

uko:validUntil a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:dateTime .

uko:isCurrent a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:boolean .

uko:isRevisionOf a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Links a revised node to its predecessor." .

Layer 1: Domain Specializations

Layer 1 specializes the universal Layer 0 foundation for four domains: general software (uko-code:), documents (uko-doc:), data schemas (uko-data:), and infrastructure (uko-infra:). Each domain defines its own classes, relationships, properties, and a DetailLevelMap mapping named levels to integer DetailDepth values.

General Software (uko-code:):

@prefix uko-code: <https://cleveragents.ai/ontology/uko/code#> .

uko-code:Module a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A source code module (file, package, namespace)." .

uko-code:Callable a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "Any callable unit (function, method, procedure, lambda)." .

uko-code:TypeDefinition a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A type or schema definition (class, struct, interface, enum, typedef)." .

uko-code:TestCase a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A test case or test function." .

uko-code:Import a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "An import/include/require statement." .

uko-code:hasReturnType a owl:DatatypeProperty ; rdfs:domain uko-code:Callable ; rdfs:range xsd:string .

uko-code:hasParameters a owl:DatatypeProperty ; rdfs:domain uko-code:Callable ; rdfs:range xsd:string ; rdfs:comment "JSON-encoded parameter list." .

uko-code:testsCallable a owl:ObjectProperty ; rdfs:domain uko-code:TestCase ; rdfs:range uko-code:Callable ; rdfs:comment "Links a test case to the callable it tests." .

Documents (uko-doc:) — Layer 1:

The document ontology represents structured and semi-structured documents (articles, technical papers, documentation, reports) as a hierarchy of containers and atoms. A key design principle is semantic awareness: when the system processes a document, it does not merely parse the structural hierarchy (sections, subsections, paragraphs) — it also analyzes the content of each text unit to infer implicit relationships. If a paragraph discusses a concept that is the subject of another section (even without an explicit hyperlink or mention by name), the RDF graph captures that relationship via uko:references or uko-doc:discussesTopic edges. This enables strategies to surface related content that a purely structural traversal would miss.

@prefix uko-doc: <https://cleveragents.ai/ontology/uko/doc#> .

# ── Container Classes ────────────────────────────────────────────── uko-doc:Document a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A complete document (article, report, manual, specification)." .

uko-doc:Part a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A top-level division of a document (e.g., Part I, Part II)." .

uko-doc:Chapter a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A chapter within a document or part." .

uko-doc:Section a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A numbered or titled section within a chapter." .

uko-doc:Subsection a owl:Class ; rdfs:subClassOf uko-doc:Section ; rdfs:comment "A subsection nested within a section (arbitrary depth)." .

# ── Atom Classes ─────────────────────────────────────────────────── uko-doc:Paragraph a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A paragraph of prose text." .

uko-doc:CodeBlock a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "An inline code listing or example within a document." .

uko-doc:Figure a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A figure, diagram, or image with an optional caption." .

uko-doc:Table a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A tabular data element within a document." .

uko-doc:BlockQuote a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A quoted passage from another source." .

uko-doc:ListBlock a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "An ordered or unordered list." .

# ── Annotation Classes ───────────────────────────────────────────── uko-doc:Footnote a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A footnote or endnote." .

uko-doc:Citation a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A bibliographic citation or reference." .

uko-doc:Comment a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "An editorial comment or annotation (e.g., HTML comment, review note)." .

# ── Boundary Classes ─────────────────────────────────────────────── uko-doc:TableOfContents a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "The document's table of contents — an interface into the document's structure." .

uko-doc:Index a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A back-of-book index or keyword index." .

uko-doc:Glossary a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A glossary of terms defined in the document." .

# ── Document-Specific Relationships ──────────────────────────────── uko-doc:discussesTopic a owl:ObjectProperty ; rdfs:subPropertyOf uko:references ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Semantic relationship: this unit discusses a topic that is the subject of another unit. Inferred by the document analyzer even when no explicit link exists." .

uko-doc:cites a owl:ObjectProperty ; rdfs:subPropertyOf uko:references ; rdfs:domain uko:InformationUnit ; rdfs:range uko-doc:Citation ; rdfs:comment "This unit cites a bibliographic reference." .

uko-doc:crossReferences a owl:ObjectProperty ; rdfs:subPropertyOf uko:references ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Explicit cross-reference (e.g., 'see Section 3.2')." .

uko-doc:precedes a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Reading order: this unit comes before the target unit." .

# ── Document-Specific Properties ─────────────────────────────────── uko-doc:headingLevel a owl:DatatypeProperty ; rdfs:domain uko-doc:Section ; rdfs:range xsd:integer ; rdfs:comment "The nesting depth of this section (1 = top-level, 2 = subsection, etc.)." .

uko-doc:headingText a owl:DatatypeProperty ; rdfs:domain uko-doc:Section ; rdfs:range xsd:string ; rdfs:comment "The title/heading text of this section." .

uko-doc:wordCount a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:integer ; rdfs:comment "Word count of the text content in this unit." .

uko-doc:language a owl:DatatypeProperty ; rdfs:domain uko-doc:Document ; rdfs:range xsd:string ; rdfs:comment "Natural language of the document (e.g., 'en', 'fr')." .

uko-doc:topicKeywords a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "JSON-encoded list of topic keywords extracted from this unit." .

Semantic Inference for Documents: When the document analyzer processes a document, it performs semantic analysis beyond pure structural parsing:

  1. Topic extraction: Each paragraph, section, and subsection is analyzed for key topics. These become uko-doc:topicKeywords properties.
  2. Implicit cross-references: If Paragraph A in Section 2 discusses "authentication flow" and Section 5 is titled "Authentication Architecture", the analyzer creates a uko-doc:discussesTopic edge from Paragraph A to Section 5 — even though no explicit link exists in the source document.
  3. Citation resolution: Bibliographic citations are linked to the paragraphs that cite them.
  4. Concept clustering: Paragraphs discussing related concepts across different sections are linked via uko:references, enabling strategies to pull in contextually relevant content from distant parts of a document.

This semantic awareness means that a breadth-depth-navigator strategy operating on a document resource can follow not just the structural containment hierarchy but also the semantic topic graph, surfacing related sections that an author would need to consider when modifying any part of the document.

Document DetailLevelMap Refinements (per-type rendering at selected depths — see Core Data Types for the authoritative uko-doc: depth map):

Depth Named Level uko-doc:Document uko-doc:Section uko-doc:Paragraph
0 TITLE_ONLY Document title only Heading text only — (not individually listed)
1 TABLE_OF_CONTENTS_L1 Title + top-level section headings Heading + child section headings First sentence only
4 TOC_WITH_SUMMARIES All headings + one-sentence abstract per section Heading + paragraph count + topic keywords + abstract First two sentences + topic keywords
8 STRUCTURAL_DETAIL Full TOC + figure/table captions + list item headers Heading + first sentence of each paragraph + all captions Full text with inline code and links preserved
10 FULL_CONTENT Complete document content Complete section content including all children Complete paragraph text with all formatting

Data Schemas (uko-data:) — Layer 1:

The data schema ontology represents databases, data warehouses, and structured data stores. It models the schema structure (schemas, tables, columns, constraints) and captures query-time relationships (views referencing tables, foreign keys, stored procedures accessing tables). Like the document ontology, the system performs semantic analysis: when a stored procedure references columns from multiple tables, those dependency edges are created automatically.

@prefix uko-data: <https://cleveragents.ai/ontology/uko/data#> .

# ── Container Classes ────────────────────────────────────────────── uko-data:Database a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A database instance containing schemas." .

uko-data:Schema a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A database schema (namespace for tables, views, etc.)." .

uko-data:Table a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A database table containing columns." .

uko-data:View a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A database view (virtual table defined by a query)." .

# ── Atom Classes ─────────────────────────────────────────────────── uko-data:Column a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A column within a table or view." .

uko-data:StoredProcedure a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A stored procedure or function in the database." .

uko-data:Trigger a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A database trigger." .

uko-data:Migration a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A schema migration (DDL change script)." .

# ── Annotation Classes ───────────────────────────────────────────── uko-data:Constraint a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A column or table constraint (NOT NULL, UNIQUE, CHECK, etc.)." .

uko-data:Index a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A database index on one or more columns." .

uko-data:ColumnComment a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A COMMENT ON COLUMN annotation in the database." .

# ── Boundary Classes ─────────────────────────────────────────────── uko-data:ForeignKey a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A foreign key constraint — an interface point between tables." .

uko-data:APIEndpoint a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A database-level API endpoint (e.g., PostgREST, GraphQL)." .

# ── Data-Specific Relationships ──────────────────────────────────── uko-data:foreignKeyTo a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:Column ; rdfs:range uko-data:Column ; rdfs:comment "Foreign key relationship from this column to a column in another table." .

uko-data:viewReferences a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:View ; rdfs:range uko-data:Table ; rdfs:comment "This view's query references columns from the target table." .

uko-data:procedureAccesses a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:StoredProcedure ; rdfs:range uko-data:Table ; rdfs:comment "This stored procedure reads from or writes to the target table." .

uko-data:triggerFires a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:Trigger ; rdfs:range uko-data:Table ; rdfs:comment "This trigger fires on events on the target table." .

uko-data:migrationAlters a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:Migration ; rdfs:range uko-data:Table ; rdfs:comment "This migration modifies the schema of the target table." .

# ── Data-Specific Properties ────────────────────────────────────── uko-data:dataType a owl:DatatypeProperty ; rdfs:domain uko-data:Column ; rdfs:range xsd:string ; rdfs:comment "SQL data type (e.g., 'VARCHAR(255)', 'INTEGER', 'JSONB')." .

uko-data:isNullable a owl:DatatypeProperty ; rdfs:domain uko-data:Column ; rdfs:range xsd:boolean .

uko-data:isPrimaryKey a owl:DatatypeProperty ; rdfs:domain uko-data:Column ; rdfs:range xsd:boolean .

uko-data:rowEstimate a owl:DatatypeProperty ; rdfs:domain uko-data:Table ; rdfs:range xsd:long ; rdfs:comment "Estimated row count from database statistics." .

uko-data:viewDefinition a owl:DatatypeProperty ; rdfs:domain uko-data:View ; rdfs:range xsd:string ; rdfs:comment "The SQL query that defines this view." .

uko-data:procedureBody a owl:DatatypeProperty ; rdfs:domain uko-data:StoredProcedure ; rdfs:range xsd:string ; rdfs:comment "The SQL/PL body of the stored procedure." .

Database DetailLevelMap Refinements (per-type rendering at selected depths — see Core Data Types for the authoritative uko-data: depth map):

Depth Named Level uko-data:Database uko-data:Table uko-data:Column
0 SCHEMA_LISTING Schema names only — (not individually listed)
1 TABLE_LISTING Schema names + table/view names Table name only
3 TYPED_COLUMNS + column types + nullability Column names + types + nullability Name + type + nullable
5 RELATIONSHIPS + foreign key graph + row estimates + primary key + foreign key targets + row estimate + FK references + constraints
7 DDL Full CREATE TABLE DDL with constraints Full CREATE TABLE DDL + index definitions Full column DDL + all constraints
11 FULL_CATALOG Complete DDL + views + procedures + sample data Full DDL + triggers + sample rows + statistics Full DDL + value distribution + sample values

Infrastructure (uko-infra:) — Layer 1:

@prefix uko-infra: <https://cleveragents.ai/ontology/uko/infra#> .

# ── Container Classes ────────────────────────────────────────────── uko-infra:Service a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A deployable service or application." .

uko-infra:ConfigBlock a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A configuration block or section (e.g., a YAML map, TOML table)." .

uko-infra:DeploymentUnit a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A deployment unit (e.g., Kubernetes Deployment, Docker Compose service)." .

# ── Atom Classes ─────────────────────────────────────────────────── uko-infra:ConfigKey a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A configuration key-value pair." .

uko-infra:EnvironmentVariable a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "An environment variable definition." .

# ── Boundary Classes ─────────────────────────────────────────────── uko-infra:Endpoint a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A network endpoint (HTTP route, gRPC service, message queue)." .

uko-infra:Port a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A network port binding." .

# ── Infrastructure-Specific Relationships ────────────────────────── uko-infra:connectsTo a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-infra:Service ; rdfs:range uko-infra:Service ; rdfs:comment "This service connects to or depends on the target service." .

uko-infra:exposes a owl:ObjectProperty ; rdfs:domain uko-infra:Service ; rdfs:range uko-infra:Endpoint ; rdfs:comment "This service exposes the target endpoint." .

Layer 2: Paradigm / Format Specializations

Layer 2 refines Layer 1 domains with paradigm- or format-specific specializations. For code: OO, functional, and procedural paradigms. For documents: academic vs. technical. For databases: relational vs. graph. Each may extend the parent domain's DetailLevelMap with additional named levels.

Object-Oriented (uko-oo:):

@prefix uko-oo: <https://cleveragents.ai/ontology/uko/oo#> .

uko-oo:Class a owl:Class ; rdfs:subClassOf uko-code:TypeDefinition, uko:Container ; rdfs:comment "An OO class that contains methods and attributes." .

uko-oo:Interface a owl:Class ; rdfs:subClassOf uko-code:TypeDefinition, uko:Boundary ; rdfs:comment "An interface or abstract base class." .

uko-oo:Method a owl:Class ; rdfs:subClassOf uko-code:Callable .

uko-oo:Attribute a owl:Class ; rdfs:subClassOf uko:Atom .

uko-oo:inheritsFrom a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-oo:Class ; rdfs:range uko-oo:Class .

uko-oo:implements a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-oo:Class ; rdfs:range uko-oo:Interface .

Functional (uko-func:):

@prefix uko-func: <https://cleveragents.ai/ontology/uko/func#> .

uko-func:PureFunction a owl:Class ; rdfs:subClassOf uko-code:Callable .

uko-func:TypeClass a owl:Class ; rdfs:subClassOf uko-code:TypeDefinition, uko:Boundary .

uko-func:Monad a owl:Class ; rdfs:subClassOf uko-code:TypeDefinition .

Code DetailLevelMap Refinements by Paradigm and Language:

The authoritative DetailLevelMap definitions — with full integer depth assignments and named level descriptions for each domain — are specified in Core Concepts > Advanced Context Management System (ACMS) > Core Data Types > DetailDepth and DetailLevelMap. The tables below show how paradigm-specific (Layer 2) and language-specific (Layer 3) UKO node types map their content at selected depth levels from the parent domain's map. These are illustrative excerpts showing what content each node type contributes at key depths, not exhaustive depth-by-depth definitions.

General Software (uko-code:) — Layer 1 (per-type rendering at selected depths):

Depth Named Level uko-code:Module uko-code:Callable uko-code:TypeDefinition
0 MODULE_LISTING Module name only — (not individually listed) — (not individually listed)
2 MEMBER_LISTING + names of top-level members Function name (no signature) Type name + kind (class/struct/enum)
4 SIGNATURES + all top-level signatures Name + parameter types + return type Name + field types + method signatures
6 STRUCTURAL_OUTLINE + control flow outline of callables Signature + control flow outline (branches, loops) Name + all field + method signatures + inheritance
9 FULL_SOURCE Complete module source Complete function body Complete type definition

Object-Oriented (uko-oo:) — Layer 2 (per-type rendering at selected depths):

Depth Named Level uko-oo:Class uko-oo:Method uko-oo:Interface
2 MEMBER_LISTING Class name + parent classes Method name + visibility Interface name
3 CLASS_HIERARCHY (OO-inserted) + inheritance chain + interface implementations + method signatures
4 SIGNATURES + attribute types + method signatures + params + return type + visibility + all method signatures with param types
5 SIGNATURES_WITH_DOCS + class docstring + attribute descriptions + docstring + side effect hints + description + implementor count
9+ FULL_SOURCE Complete class definition Complete method body Complete interface definition

Functional (uko-func:) — Layer 2 (per-type rendering at selected depths):

Depth Named Level uko-func:PureFunction uko-func:TypeClass uko-func:Monad
2 MEMBER_LISTING Function name Type class name Monad name
4 SIGNATURES Name + type signature + purity annotation Name + associated types Name + bind/return types
6 STRUCTURAL_OUTLINE + pattern match structure + guard conditions + all method signatures + laws + bind/return implementations
9 FULL_SOURCE Complete function definition Complete type class definition Complete monad definition

Procedural (uko-proc:) — Layer 2 (rendering at selected depths):

Depth Named Level Procedural Specifics
2 MEMBER_LISTING Function names + global variable names (no types)
4 SIGNATURES Function declarations + global variable types + header file exports
5 SIGNATURES_WITH_DOCS + function descriptions + global variable purposes + header summaries
6 STRUCTURAL_OUTLINE + function signatures with parameter names + struct definitions + macro signatures
9 FULL_SOURCE Complete source including all function bodies, macros, and inline assembly
Layer 3: Technology-Specific Specializations

Layer 3 extends Layer 2 (or Layer 1 directly) with technology-specific refinements — e.g., Python, TypeScript, Rust, Java for code; PostgreSQL, MongoDB for databases; Markdown, LaTeX for documents. Each may insert additional named DetailLevelMap levels into its parent's map.

Language-Specific (Layer 3) DetailLevelMap insertions — examples:

Each language extension may insert additional named levels into its parent paradigm's map, shifting subsequent depths upward to maintain consecutive integer numbering (see the fully expanded effective maps in Core Data Types). The table below summarizes the key additions:

Language Inserted Levels Additional Content at Those Depths
Python (uko-py:) DECORATED_SIGNATURES (between SIGNATURES_WITH_DOCS and STRUCTURAL_OUTLINE), TYPE_STUBS (between KEY_LOGIC and NEAR_COMPLETE), WITH_TESTS (beyond FULL_SOURCE) Decorator chains (@property, @staticmethod, custom), .pyi stub type annotations, associated test cases
TypeScript (uko-ts:) (extends at SIGNATURES level) export/default export markers, declare ambient types, generic type parameters, mapped/conditional types
Rust (uko-rs:) (extends at SIGNATURES level) Visibility modifiers (pub, pub(crate)), lifetime parameters, trait bounds, unsafe markers, #[derive] macros, #[must_use] annotations
Java (uko-java:) (extends at SIGNATURES level) Package visibility, final/abstract/sealed modifiers, annotation lists (@Override, @Deprecated), checked exceptions

Not every language requires a Layer 3 extension. If a language's semantics are fully captured by the Layer 1 (uko-code:) and Layer 2 (paradigm) maps, no Layer 3 extension is needed. The system falls back to the nearest parent layer's DetailLevelMap.

The Universal View Guarantee

Because every domain-specific class ultimately inherits from Layer 0 (uko:InformationUnit, uko:Container, etc.), any strategy or query written against Layer 0 automatically works across all domains — not just different programming languages, but across code, documents, databases, and infrastructure alike. For example:

# "Find all containers that depend on this atom" — works for Python classes,
# TypeScript modules, Rust crates, SQL tables, document sections, or config blocks.
SELECT ?container WHERE {
    ?container a uko:Container .
    ?container uko:dependsOn <target_atom> .
}

Similarly, a DetailDepth request at Layer 0 is resolved by the most specific DetailLevelMap available in the inheritance chain. A request for depth 3 of a uko:Container will produce a member summary with one-line docstrings for a Python module (MEMBER_SUMMARY), a full table of contents for a document chapter (FULL_TOC), or typed column definitions for a database table (TYPED_COLUMNS) — all through the same universal integer-depth interface, with each domain mapping the integer to its domain-appropriate rendering.

Provenance Contract

Every UKO node carries provenance back to the originating resource:

uko-py:class/AuthManager
    uko:sourceResource  <resource-ulid-01HXM7...>
    uko:sourcePath      "src/auth/manager.py"
    uko:sourceRange     "15:1-87:0"
    uko:validFrom       "2026-01-15T10:30:00Z"
    uko:isCurrent       true

This allows the PreambleGenerator pipeline component to generate provenance summaries in the context preamble, and allows actors to trace any piece of context back to its exact source location.

Temporal Data Model and Storage Tiers

Revision-Aware RDF

Each UKO node carries temporal metadata. When code changes, existing nodes are not deleted — instead, their validUntil is set and isCurrent becomes false, and a new node is created with isRevisionOf pointing to the predecessor:

# Before code change:
uko-py:class/AuthManager_v1
    uko:isCurrent true ;
    uko:validFrom "2026-01-10T00:00:00Z"^^xsd:dateTime .

# After code change: uko-py:class/AuthManager_v1 uko:isCurrent false ; uko:validFrom "2026-01-10T00:00:00Z"^^xsd:dateTime ; uko:validUntil "2026-01-15T10:30:00Z"^^xsd:dateTime .

uko-py:class/AuthManager_v2 uko:isCurrent true ; uko:validFrom "2026-01-15T10:30:00Z"^^xsd:dateTime ; uko:isRevisionOf uko-py:class/AuthManager_v1 .

This temporal chain is what enables the temporal-archaeology strategy to find patterns like "this class was refactored 3 times in the last month" or "this function's signature changed after we updated the auth library."

Three Storage Tiers with Temporal Alignment
Tier Content Retention Access Pattern Temporal
Hot Current UKO graph + recent embeddings + active text index Until resource removed Direct query via BAL isCurrent = true only
Warm Recent decision contexts, plan context snapshots context.tiers.warm.retention-hours (default: 24h) Scoped query via plan hierarchy Current + recently-expired nodes
Cold Archived decision contexts, historical UKO snapshots context.tiers.cold.retention-days (default: 90d) Full historical query All temporal versions

Scoped Views and Plan Subgraph Projection

Resource Scope Resolution

When a plan is created (via agents plan use), the system resolves its resource scope:

class ResourceScopeResolver:
    """Determines which resources a plan can see."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">resolve</span>(<span style="color: cyan;">self</span>, plan: <span style="color: cyan;">Plan</span>) -> <span style="color: cyan;">ResourceScope</span>:
    <span style="color: #888;"># 1. Get all projects this plan targets</span>
    projects = plan.target_projects
    <span style="color: #888;"># 2. Collect all resources linked to those projects</span>
    resources = <span style="color: cyan;">set</span>()
    <span style="color: magenta; font-weight: 600;">for</span> project <span style="color: magenta; font-weight: 600;">in</span> projects:
        resources.update(project.linked_resources)
    <span style="color: #888;"># 3. Expand to include child resources (DAG traversal)</span>
    expanded = <span style="color: cyan;">set</span>()
    <span style="color: magenta; font-weight: 600;">for</span> resource <span style="color: magenta; font-weight: 600;">in</span> resources:
        expanded.add(resource)
        expanded.update(<span style="color: cyan;">self</span>.registry.get_descendants(resource))
    <span style="color: #888;"># 4. Apply context view filters (include/exclude)</span>
    view = <span style="color: cyan;">self</span>._resolve_view(plan)
    filtered = <span style="color: cyan;">self</span>._apply_filters(expanded, view)
    <span style="color: magenta; font-weight: 600;">return</span> ResourceScope(resources=filtered, projects=projects)

UKO Subgraph Projection

Given a resource scope, the system projects only the relevant UKO subgraph:

class UKOSubgraphProjector:
    """Projects the UKO graph to only include nodes from scoped resources."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">project</span>(<span style="color: cyan;">self</span>, scope: <span style="color: cyan;">ResourceScope</span>,
            graph_backend: <span style="color: cyan;">GraphBackend</span>) -> <span style="color: cyan;">ProjectedGraph</span>:
    <span style="color: #888;"># Only include UKO nodes whose sourceResource is in scope</span>
    <span style="color: magenta; font-weight: 600;">return</span> graph_backend.subgraph_by_resources(
        resource_ulids=[r.ulid <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> scope.resources],
        include_temporal=scope.temporal_scope,
    )

Scoped Backend Views

Each backend (text, vector, graph) is wrapped in a ScopedView that automatically filters queries to only return results from in-scope resources:

class ScopedBackendView:
    """Wraps a backend to restrict queries to a resource scope."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__init__</span>(<span style="color: cyan;">self</span>, backend, scope: <span style="color: cyan;">ResourceScope</span>):
    <span style="color: cyan;">self</span>.backend = backend
    <span style="color: cyan;">self</span>.scope = scope
    <span style="color: cyan;">self</span>._resource_filter = {r.ulid <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> scope.resources}

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">search</span>(<span style="color: cyan;">self</span>, query, **kwargs):
    <span style="color: #888;"># Inject the resource filter into the backend query</span>
    results = <span style="color: cyan;">self</span>.backend.search(
        query,
        resource_filter=<span style="color: cyan;">self</span>._resource_filter,
        **kwargs,
    )
    <span style="color: magenta; font-weight: 600;">return</span> results

This is critical for multi-project isolation: a plan targeting local/api-service never sees resources from local/frontend, even if both projects share the same physical database indices.

Backend Abstraction Layer (BAL)

The BAL provides a uniform interface to heterogeneous data stores. Each backend type has a protocol:

Backend Protocols
class TextBackend(Protocol):
    def search(self, query: str, *, resource_filter: set[str],
               limit: int = 20) -> list[TextResult]: ...
    def index(self, resource_ulid: str, content: str,
              metadata: dict) -> None: ...

class VectorBackend(Protocol): def search(self, embedding: list[float], *, resource_filter: set[str], limit: int = 20, threshold: float = 0.3) -> list[VectorResult]: ... def index(self, resource_ulid: str, chunks: list[EmbeddingChunk]) -> None: ...

class GraphBackend(Protocol): def query_sparql(self, sparql: str, *, resource_filter: set[str]) -> list[dict]: ... def add_triples(self, triples: list[Triple]) -> None: ... def subgraph_by_resources(self, resource_ulids: list[str], include_temporal: TemporalScope) -> ProjectedGraph: ... def traverse(self, start_uri: str, max_hops: int, edge_types: list[str], resource_filter: set[str]) -> SubGraph: ...

Unified Result Types
@dataclass
class TextResult:
    resource_ulid: str
    path: str
    line_range: tuple[int, int]
    content: str
    score: float

@dataclass class VectorResult: resource_ulid: str uko_node: str content: str similarity: float metadata: dict

Context Assembly Pipeline

The context assembly pipeline is the central orchestrator of context assembly. It receives a ContextRequest, processes it through a chain of pluggable components, and produces a budget-respecting AssembledContext. Every stage of the pipeline is a replaceable plugin — each defined by a Protocol interface, shipped with a default implementation, and configurable at the global, project, or plan scope.

Pipeline Component Model

The pipeline follows the Pipes and Filters architectural pattern, with each filter implemented as a Strategy (GoF) that can be swapped at runtime. Component resolution uses a Chain of Responsibility pattern: plan-level overrides take precedence over project-level, which take precedence over global defaults.


Pipeline Component Resolution Order:

plan scope ──► project scope ──► global scope ──► built-in default (most specific) (least specific)

There are ten pluggable components, grouped into three pipeline phases:

Phase 1 — Strategy Orchestration (selects and runs context strategies):

# Component Protocol Responsibility
1 StrategySelector StrategySelectorProtocol Decides which strategies to invoke and computes confidence scores
2 BudgetAllocator BudgetAllocatorProtocol Distributes the token budget across selected strategies
3 StrategyExecutor StrategyExecutorProtocol Controls how strategies are invoked (parallelism, timeouts, error handling)

Phase 2 — Fragment Fusion (merges strategy outputs into a coherent payload):

# Component Protocol Responsibility
4 FragmentDeduplicator FragmentDeduplicatorProtocol Removes duplicate fragments across strategy outputs
5 DetailDepthResolver DetailDepthResolverProtocol Resolves conflicts when the same UKO node appears at different depths
6 FragmentScorer FragmentScorerProtocol Computes composite relevance scores for ranking
7 BudgetPacker BudgetPackerProtocol Fits scored fragments into the token budget
8 FragmentOrderer FragmentOrdererProtocol Orders final fragments for coherence in the prompt

Phase 3 — Context Finalization (produces the final deliverable):

# Component Protocol Responsibility
9 PreambleGenerator PreambleGeneratorProtocol Generates the context preamble (provenance summary, structure map)
10 SkeletonCompressor SkeletonCompressorProtocol Compresses parent context into a skeleton for child plan inheritance
Component Protocol Definitions

Each component is defined as a @runtime_checkable Protocol, enabling both structural subtyping (duck typing) and explicit isinstance checks. The protocols use Generic type parameters where applicable, and employ the Template Method pattern — each protocol defines the contract while default implementations provide the algorithm skeleton with overridable hook methods.

# ── Phase 1: Strategy Orchestration ────────────────────────────────

@runtime_checkable class StrategySelectorProtocol(Protocol): """Decides which strategies to invoke and with what confidence. Applies request preferences and backend availability filtering."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">select</span>(
    <span style="color: cyan;">self</span>,
    strategies: <span style="color: cyan;">Sequence</span>[ContextStrategy],
    request: ContextRequest,
    backends: BackendSet,
) -> <span style="color: cyan;">list</span>[<span style="color: cyan;">tuple</span>[ContextStrategy, <span style="color: cyan;">float</span>]]:
    <span style="color: #888;">"""Returns (strategy, confidence) pairs, sorted by priority.
    Confidence is 0.0-1.0. Strategies with 0.0 are excluded."""</span>
    ...

@runtime_checkable class BudgetAllocatorProtocol(Protocol): """Distributes the token budget across selected strategies. May use proportional, priority-weighted, or custom allocation schemes."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">allocate</span>(
    <span style="color: cyan;">self</span>,
    candidates: <span style="color: cyan;">list</span>[<span style="color: cyan;">tuple</span>[ContextStrategy, <span style="color: cyan;">float</span>]],
    total_budget: <span style="color: cyan;">int</span>,
    request: ContextRequest,
) -> <span style="color: cyan;">list</span>[<span style="color: cyan;">tuple</span>[ContextStrategy, <span style="color: cyan;">float</span>, <span style="color: cyan;">int</span>]]:
    <span style="color: #888;">"""Returns (strategy, confidence, allocated_tokens) triples.
    Sum of allocated_tokens must not exceed total_budget."""</span>
    ...

@runtime_checkable class StrategyExecutorProtocol(Protocol): """Controls how strategies are invoked — parallelism model, timeout handling, circuit breaking, and error recovery."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">execute</span>(
    <span style="color: cyan;">self</span>,
    allocations: <span style="color: cyan;">list</span>[<span style="color: cyan;">tuple</span>[ContextStrategy, <span style="color: cyan;">float</span>, <span style="color: cyan;">int</span>]],
    request: ContextRequest,
    backends: BackendSet,
    plan_context: PlanContext,
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Executes strategies and collects all fragments.
    Must handle strategy failures gracefully (log and continue)."""</span>
    ...

# ── Phase 2: Fragment Fusion ────────────────────────────────────────

@runtime_checkable class FragmentDeduplicatorProtocol(Protocol): """Removes duplicate fragments from the merged strategy output. Deduplication can be identity-based, content-hash-based, or semantic."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">deduplicate</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Returns deduplicated fragments. When duplicates are found,
    the fragment with the highest relevance_score is retained."""</span>
    ...

@runtime_checkable class DetailDepthResolverProtocol(Protocol): """Resolves conflicts when the same UKO node appears at different detail depths across strategy outputs."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">resolve</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
    budget: <span style="color: cyan;">int</span>,
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Returns fragments with depth conflicts resolved.
    Default: keep highest depth that fits within budget."""</span>
    ...

@runtime_checkable class FragmentScorerProtocol(Protocol): """Computes a composite score for each fragment, used for ranking during budget packing. Applies hierarchical weighting, strategy quality weighting, and recency bonuses."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">score</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
    plan_context: PlanContext,
) -> <span style="color: cyan;">list</span>[ScoredFragment]:
    <span style="color: #888;">"""Returns fragments annotated with composite scores.
    ScoredFragment adds a `composite_score: float` field."""</span>
    ...

@runtime_checkable class BudgetPackerProtocol(Protocol): """Fits scored fragments into the token budget using a packing algorithm. Supports depth fallback — if a fragment doesn't fit at its current depth, it can be re-rendered at a lower depth."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">pack</span>(
    <span style="color: cyan;">self</span>,
    scored_fragments: <span style="color: cyan;">list</span>[ScoredFragment],
    budget: <span style="color: cyan;">int</span>,
    detail_level_maps: DetailLevelMapRegistry,
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Returns the subset of fragments that fit within budget,
    possibly re-rendered at lower depths. Ordered by score."""</span>
    ...

@runtime_checkable class FragmentOrdererProtocol(Protocol): """Orders the packed fragments for optimal coherence in the actor's context window. Ordering may be by relevance, by topological dependency, or by a hybrid coherence heuristic."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">order</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Returns fragments in the final presentation order."""</span>
    ...

# ── Phase 3: Context Finalization ──────────────────────────────────

@runtime_checkable class PreambleGeneratorProtocol(Protocol): """Generates the preamble prepended to assembled context — a structured summary of what's included, why, and from where."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">generate</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
    strategies_used: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>],
    budget_used: <span style="color: cyan;">float</span>,
    max_tokens: <span style="color: cyan;">int</span>,
) -> <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #888;">"""Returns the preamble string, or None if preamble is disabled."""</span>
    ...

@runtime_checkable class SkeletonCompressorProtocol(Protocol): """Compresses a parent plan's assembled context into a compact skeleton representation for inheritance by child plans."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">compress</span>(
    <span style="color: cyan;">self</span>,
    parent_context: AssembledContext,
    child_focus: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>],
    skeleton_budget: <span style="color: cyan;">int</span>,
) -> AssembledContext:
    <span style="color: #888;">"""Returns a compressed version of the parent context that
    fits within skeleton_budget tokens. Typically reduces all
    fragments to depth 0-1 (e.g., MODULE_LISTING / TITLE_ONLY)."""</span>
    ...

Supporting Types
@dataclass(frozen=True)
class ScoredFragment:
    """A ContextFragment annotated with a composite score by the FragmentScorer."""
    fragment: ContextFragment
    composite_score: float        # 0.0-1.0 composite ranking score
    score_components: dict[str, float]  # Breakdown: {"relevance": 0.9, "hierarchy": 0.8, ...}

@dataclass class PipelineConfig: """Resolved configuration for the context assembly pipeline. Each field holds the concrete plugin instance to use for that stage. Resolved via the scope chain: plan > project > global > built-in."""

strategy_selector: StrategySelectorProtocol
budget_allocator: BudgetAllocatorProtocol
strategy_executor: StrategyExecutorProtocol
fragment_deduplicator: FragmentDeduplicatorProtocol
detail_depth_resolver: DetailDepthResolverProtocol
fragment_scorer: FragmentScorerProtocol
budget_packer: BudgetPackerProtocol
fragment_orderer: FragmentOrdererProtocol
preamble_generator: PreambleGeneratorProtocol
skeleton_compressor: SkeletonCompressorProtocol

Pipeline Orchestrator

The ContextAssemblyPipeline is the top-level orchestrator that wires the ten components together. It implements the Mediator pattern — components do not communicate directly with each other; the pipeline mediates all data flow. The pipeline itself is stateless; all state is carried through the data flowing between stages.

class ContextAssemblyPipeline:
    """Mediator that orchestrates the 10-stage context assembly pipeline.
    Each stage is a pluggable component resolved from the scope chain."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__init__</span>(<span style="color: cyan;">self</span>, config: PipelineConfig, strategies: <span style="color: cyan;">Sequence</span>[ContextStrategy]):
    <span style="color: cyan;">self</span>._config = config
    <span style="color: cyan;">self</span>._strategies = strategies

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">assemble</span>(
    <span style="color: cyan;">self</span>,
    request: ContextRequest,
    backends: BackendSet,
    budget: <span style="color: cyan;">int</span>,
    plan_context: PlanContext,
) -> AssembledContext:
    <span style="color: #888;"># ── Phase 1: Strategy Orchestration ──────────────────────</span>
    <span style="color: #888;"># 1. Select strategies</span>
    candidates = <span style="color: cyan;">self</span>._config.strategy_selector.select(
        <span style="color: cyan;">self</span>._strategies, request, backends,
    )
    <span style="color: #888;"># 2. Allocate budget</span>
    allocations = <span style="color: cyan;">self</span>._config.budget_allocator.allocate(
        candidates, budget, request,
    )
    <span style="color: #888;"># 3. Execute strategies</span>
    raw_fragments = <span style="color: cyan;">self</span>._config.strategy_executor.execute(
        allocations, request, backends, plan_context,
    )

    <span style="color: #888;"># ── Phase 2: Fragment Fusion ──────────────────────────────</span>
    <span style="color: #888;"># 4. Deduplicate</span>
    deduped = <span style="color: cyan;">self</span>._config.fragment_deduplicator.deduplicate(raw_fragments)
    <span style="color: #888;"># 5. Resolve depth conflicts</span>
    resolved = <span style="color: cyan;">self</span>._config.detail_depth_resolver.resolve(deduped, budget)
    <span style="color: #888;"># 6. Score fragments</span>
    scored = <span style="color: cyan;">self</span>._config.fragment_scorer.score(resolved, plan_context)
    <span style="color: #888;"># 7. Pack into budget</span>
    packed = <span style="color: cyan;">self</span>._config.budget_packer.pack(
        scored, budget, <span style="color: cyan;">self</span>._detail_level_maps,
    )
    <span style="color: #888;"># 8. Order for coherence</span>
    ordered = <span style="color: cyan;">self</span>._config.fragment_orderer.order(packed)

    <span style="color: #888;"># ── Phase 3: Context Finalization ─────────────────────────</span>
    <span style="color: #888;"># 9. Generate preamble</span>
    strategies_used = [s.name <span style="color: magenta; font-weight: 600;">for</span> s, _, _ <span style="color: magenta; font-weight: 600;">in</span> allocations]
    preamble = <span style="color: cyan;">self</span>._config.preamble_generator.generate(
        ordered, strategies_used,
        budget_used=<span style="color: cyan;">sum</span>(f.token_count <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> ordered) / budget,
        max_tokens=<span style="color: cyan;">self</span>._preamble_max_tokens,
    )

    <span style="color: magenta; font-weight: 600;">return</span> AssembledContext(
        fragments=ordered,
        total_tokens=<span style="color: cyan;">sum</span>(f.token_count <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> ordered),
        budget_used=<span style="color: cyan;">sum</span>(f.token_count <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> ordered) / budget,
        strategies_used=strategies_used,
        context_hash=<span style="color: cyan;">self</span>._compute_hash(ordered),
        preamble=preamble,
        provenance_map={f.uko_node: f.provenance <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> ordered},
    )

Built-in Default Implementations

Each component ships with a default implementation that provides the behavior described in the current specification. Custom implementations need only implement the relevant Protocol — they need not subclass the default.

Phase 1 defaults:

Component Default Class Algorithm
StrategySelector ConfidenceWeightedSelector Calls can_handle() on all registered strategies; filters to confidence > 0; applies ContextRequest.preferred_strategies preferences; sorts by confidence * quality_score descending. Uses the Observer pattern to emit StrategySelectionEvent for diagnostics.
BudgetAllocator ProportionalBudgetAllocator Allocates budget proportional to confidence * quality_score. Guarantees each strategy receives at least min_useful_budget tokens or is excluded. Implements Flyweight for allocation metadata reuse across re-assembly cycles.
StrategyExecutor ParallelStrategyExecutor Runs strategies concurrently via ThreadPoolExecutor. Per-strategy timeout (default: 30s). Uses Circuit Breaker pattern: after 3 consecutive failures, a strategy is temporarily disabled for that plan. Failed strategies log warnings and are excluded from results.

Phase 2 defaults:

Component Default Class Algorithm
FragmentDeduplicator ContentHashDeduplicator Groups fragments by UKO node URI; within each group, hashes content to detect duplicates; retains the fragment with the highest relevance_score. Configurable via context.fusion.dedup-strategy: content-hash (default), uko-identity (URI only), or semantic (embedding cosine similarity > 0.95). Implements Strategy (GoF) pattern to swap dedup algorithms.
DetailDepthResolver MaxDepthResolver When the same UKO node appears at multiple depths, retains the highest-depth rendering that fits within the remaining budget. Uses a Specification pattern: each fragment is tested against a budget specification, and the highest-depth satisfying fragment wins.
FragmentScorer WeightedCompositeScorer Computes composite = relevance_score * strategy_quality * hierarchy_weight * recency_bonus. Hierarchy weight is derived from the fragment's distance to the plan's focus nodes. Recency bonus is 1.0 for current nodes, decaying for older revisions. Scoring weights are configurable. Uses Decorator pattern to compose scoring dimensions.
BudgetPacker GreedyKnapsackPacker Sorts by composite score descending; greedily adds fragments until budget is exhausted. When a fragment doesn't fit at its current depth, attempts depth fallback: re-renders at progressively lower depths (e.g., depth 9 → 4 → 0) until it fits or is excluded. Implements Iterator pattern over the depth fallback sequence.
FragmentOrderer RelevanceCoherenceOrderer Groups fragments by resource, then orders within each group by containment depth and source position. Groups are ordered by maximum composite score within the group. Configurable via context.fusion.ordering: relevance (strict score order), topological (dependency-aware), relevance-coherence (default hybrid). Implements Template Method with overridable _group_key() and _intra_group_sort() hooks.

Phase 3 defaults:

Component Default Class Algorithm
PreambleGenerator ProvenancePreambleGenerator Generates a structured preamble listing: included resources, strategies used, budget utilization, and a compact structure map. Respects context.fusion.preamble-max-tokens limit. Can be disabled via context.fusion.preamble-enabled = false. Uses Builder pattern to construct preamble sections.
SkeletonCompressor DepthReductionCompressor Re-renders all parent context fragments at depth 0-1 (e.g., MODULE_LISTING for code, TABLE_OF_CONTENTS_L1 for documents). Prioritizes fragments closer to the child's focus area. Fits within skeleton_budget tokens (derived from skeleton_ratio). Uses Visitor pattern to traverse and re-render fragments by domain type.
Scope Resolution and Configuration

Pipeline components are resolved through a three-level scope chain. Each scope can override any subset of the ten components; unspecified components inherit from the next broader scope.

class PipelineConfigResolver:
    """Resolves the effective PipelineConfig for a given context assembly.
    Implements the Chain of Responsibility pattern across three scopes."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">resolve</span>(
    <span style="color: cyan;">self</span>,
    plan: Plan | <span style="color: magenta; font-weight: 600;">None</span>,
    project: Project | <span style="color: magenta; font-weight: 600;">None</span>,
    global_config: GlobalConfig,
) -> PipelineConfig:
    <span style="color: #888;"># 1. Start with built-in defaults</span>
    config = <span style="color: cyan;">self</span>._builtin_defaults()
    <span style="color: #888;"># 2. Apply global overrides from config.toml</span>
    config = <span style="color: cyan;">self</span>._apply_scope(config, global_config.pipeline_overrides)
    <span style="color: #888;"># 3. Apply project-level overrides (if any)</span>
    <span style="color: magenta; font-weight: 600;">if</span> project <span style="color: magenta; font-weight: 600;">and</span> project.pipeline_overrides:
        config = <span style="color: cyan;">self</span>._apply_scope(config, project.pipeline_overrides)
    <span style="color: #888;"># 4. Apply plan-level overrides (if any)</span>
    <span style="color: magenta; font-weight: 600;">if</span> plan <span style="color: magenta; font-weight: 600;">and</span> plan.pipeline_overrides:
        config = <span style="color: cyan;">self</span>._apply_scope(config, plan.pipeline_overrides)
    <span style="color: magenta; font-weight: 600;">return</span> config

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">_apply_scope</span>(<span style="color: cyan;">self</span>, base: PipelineConfig, overrides: <span style="color: cyan;">dict</span>) -> PipelineConfig:
    <span style="color: #888;">"""Merges overrides into the base config using the Prototype pattern.
    Only non-None overrides replace the base component."""</span>
    <span style="color: magenta; font-weight: 600;">return</span> PipelineConfig(**{
        field: overrides.get(field, getattr(base, field))
        <span style="color: magenta; font-weight: 600;">for</span> field <span style="color: magenta; font-weight: 600;">in</span> PipelineConfig.__dataclass_fields__
    })

Pipeline components are registered in TOML configuration:

# ── Global config.toml ────────────────────────────────────────────
[context.pipeline]
# Override any pipeline component globally (values are "module:ClassName")
strategy-selector     = "builtin:ConfidenceWeightedSelector"   # default
budget-allocator      = "builtin:ProportionalBudgetAllocator"   # default
strategy-executor     = "builtin:ParallelStrategyExecutor"      # default
fragment-deduplicator = "builtin:ContentHashDeduplicator"       # default
detail-depth-resolver = "builtin:MaxDepthResolver"              # default
fragment-scorer       = "builtin:WeightedCompositeScorer"       # default
budget-packer         = "builtin:GreedyKnapsackPacker"          # default
fragment-orderer      = "builtin:RelevanceCoherenceOrderer"     # default
preamble-generator   = "builtin:ProvenancePreambleGenerator"   # default
skeleton-compressor  = "builtin:DepthReductionCompressor"      # default

# ── Per-component configuration ──────────────────────────────────── [context.pipeline.strategy-executor] timeout-seconds = 30 max-workers = 4 circuit-breaker-threshold = 3

[context.pipeline.fragment-scorer] relevance-weight = 0.4 hierarchy-weight = 0.3 quality-weight = 0.2 recency-weight = 0.1

[context.pipeline.budget-packer] depth-fallback-steps = [9, 4, 2, 0] # depths to try during fallback min-fragment-tokens = 10 # fragments below this are excluded

Project-level and plan-level overrides use the same key structure, specified via the context view YAML or the agents project context set CLI:

# ── Project-level context view YAML ────────────────────────────────
project: local/api-service
view: strategize
pipeline:
  fragment-scorer: "my_extensions.scorers:DomainAwareScorer"
  budget-packer: "my_extensions.packers:PriorityPreservingPacker"
Fallback Degradation Path

The pipeline's ConfidenceWeightedSelector (default StrategySelector) implements automatic fallback:

  1. Try arce (requires all backends) — if unavailable:
  2. Try breadth-depth-navigator (requires graph) — if unavailable:
  3. Try semantic-embedding (requires vector) — if unavailable:
  4. Fall back to simple-keyword (requires only text search / ripgrep)

This ensures the system always produces context, even when advanced backends are not configured.

Initial Context Assembly

Before the actor's first turn, the system assembles initial context:

class InitialContextAssembler:
    """Produces the starting context for an actor invocation."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">assemble</span>(<span style="color: cyan;">self</span>, plan: <span style="color: cyan;">Plan</span>, actor: <span style="color: cyan;">Actor</span>,
             token_budget: <span style="color: cyan;">int</span>) -> <span style="color: cyan;">AssembledContext</span>:
    <span style="color: #888;"># 1. Resolve resource scope</span>
    scope = <span style="color: cyan;">self</span>.scope_resolver.resolve(plan)
    <span style="color: #888;"># 2. Get inherited context from parent plan (if any)</span>
    inherited = <span style="color: cyan;">self</span>._get_inherited_context(plan)
    <span style="color: #888;"># 3. Create scoped backend views</span>
    backends = <span style="color: cyan;">self</span>.bal.create_scoped_views(scope)
    <span style="color: #888;"># 4. Build the initial context request from plan metadata</span>
    initial_request = <span style="color: cyan;">self</span>._build_initial_request(plan, actor, inherited)
    <span style="color: #888;"># 5. Run through the Context Assembly Pipeline</span>
    result = <span style="color: cyan;">self</span>.pipeline.assemble(
        request=initial_request,
        backends=backends,
        budget=token_budget,
        plan_context=PlanContext(
            plan=plan,
            parent_context=inherited,
            depth_in_tree=plan.depth_in_tree,
        ),
    )
    <span style="color: magenta; font-weight: 600;">return</span> result

Context Inheritance Mechanism

class PlanContextInheritance:
    """Manages how child plans inherit and refine parent context.
    Uses the pipeline's SkeletonCompressor component to produce
    compact parent context for child plan inheritance."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">compute_child_context</span>(<span style="color: cyan;">self</span>, parent_plan, child_plan, parent_context):
    <span style="color: #888;"># 1. Determine the child's focus from the parent's decisions</span>
    child_focus = <span style="color: cyan;">self</span>._extract_child_focus(parent_plan, child_plan)
    <span style="color: #888;"># 2. Compute depth/breadth adjustments</span>
    depth_delta = child_plan.depth_in_tree - parent_plan.depth_in_tree
    child_detail = <span style="color: cyan;">self</span>._increase_detail(<span style="color: cyan;">self</span>._avg_detail(parent_context), depth_delta)
    child_breadth = max(<span style="color: yellow;">1</span>, parent_context.avg_breadth - depth_delta)
    <span style="color: #888;"># 3. Compute skeleton budget from the child's token budget</span>
    skeleton_budget = <span style="color: cyan;">int</span>(child_plan.token_budget * <span style="color: cyan;">self</span>.config.skeleton_ratio)
    <span style="color: #888;"># 4. Use the pipeline's SkeletonCompressor to build the parent skeleton</span>
    skeleton = <span style="color: cyan;">self</span>.pipeline.skeleton_compressor.compress(
        parent_context=parent_context,
        child_focus=child_focus,
        skeleton_budget=skeleton_budget,
    )
    <span style="color: #888;"># 5. Build the inherited context request with parent skeleton</span>
    <span style="color: magenta; font-weight: 600;">return</span> ContextRequest(
        focus=child_focus,
        breadth=child_breadth,
        depth=child_detail,
        depth_gradient=<span style="color: magenta; font-weight: 600;">True</span>,
        metadata={
            <span style="color: #66cc66;">"parent_context_hash"</span>: parent_context.context_hash,
            <span style="color: #66cc66;">"parent_skeleton"</span>: skeleton,
            <span style="color: #66cc66;">"inherited_decisions"</span>: <span style="color: cyan;">self</span>._relevant_parent_decisions(parent_plan, child_focus),
        },
    )

Skeleton Context Propagation

The skeleton is a compact, low-depth representation (depth 0-1) of the parent's context window that is passed to every child. This ensures children always have the "big picture" available, even though they focus on a narrow area. The skeleton typically consumes 5-15% of the child's token budget.


Parent's hot context (32K tokens):
  ┌─ Module: src/auth/          [depth 3/MEMBER_SUMMARY: 2K tokens]
  ├─ Module: src/core/          [depth 3/MEMBER_SUMMARY: 2K tokens]
  ├─ Module: src/api/           [depth 3/MEMBER_SUMMARY: 2K tokens]
  ├─ Module: src/services/      [depth 3/MEMBER_SUMMARY: 2K tokens]
  └─ ... 12 more modules        [depth 3/MEMBER_SUMMARY: 24K tokens]

Child's hot context (32K tokens): ┌─ [SKELETON from parent] [depth 0/MODULE_LISTING: 3K tokens] │ Module: src/auth/ [just name + member names] │ Module: src/core/ [just name + member names] │ Module: src/api/ [just name + member names] │ ... etc. ├─ [CHILD FOCUS AREA] [depth 9/FULL_SOURCE: 25K tokens] │ Class: AuthManager [depth 9: all methods with bodies] │ Class: TokenValidator [depth 9: referenced by AuthManager] │ Class: User [depth 4/SIGNATURES: used as parameter type] └─ [INHERITED DECISIONS] [4K tokens] Decision: "Use async patterns for all auth operations" Decision: "Maintain backward compat with sync callers"

Dynamic Context Window Adaptation

The context window size may change between invocations. Different LLM providers, different models, different configurations, and even dynamic adjustments during a session can alter the available budget. The ACMS handles this seamlessly.

Budget Computation
class ContextBudgetCalculator:
    """Computes the effective context budget for each assembly cycle."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">compute</span>(<span style="color: cyan;">self</span>, actor: <span style="color: cyan;">Actor</span>, plan: <span style="color: cyan;">Plan</span>,
            conversation_history_tokens: <span style="color: cyan;">int</span>) -> <span style="color: cyan;">int</span>:
    <span style="color: #888;"># 1. Get the actor's hard context window limit</span>
    model_limit = actor.model_config.context_window_tokens
    <span style="color: #888;"># 2. Reserve space for system prompt, history, tools, response</span>
    reserved = (<span style="color: cyan;">self</span>._count_tokens(actor.system_prompt) +
                conversation_history_tokens +
                <span style="color: cyan;">self</span>._estimate_tool_tokens(actor.skills) +
                actor.model_config.max_output_tokens)
    <span style="color: #888;"># 3. Apply the soft cap from context view policy</span>
    soft_cap = <span style="color: cyan;">self</span>._resolve_view(plan).hot_max_tokens <span style="color: magenta; font-weight: 600;">or</span> <span style="color: cyan;">float</span>(<span style="color: #66cc66;">'inf'</span>)
    <span style="color: #888;"># 4. Calculate available budget</span>
    effective_budget = max(<span style="color: yellow;">0</span>, min(model_limit - reserved, soft_cap))
    <span style="color: magenta; font-weight: 600;">return</span> effective_budget

Adaptive Behavior

When the budget changes between context assembly cycles:

  1. Budget increase: The pipeline re-runs strategy orchestration (Phase 1) with the expanded budget. The BudgetAllocator distributes the larger budget across strategies, and the BudgetPacker may promote fragments previously rendered at a low depth (e.g., depth 0) to a higher depth (e.g., depth 4 or 9).
  2. Budget decrease: The pipeline's BudgetPacker re-applies the knapsack with the new budget, potentially reducing fragment depths via its depth fallback mechanism or dropping low-relevance items as ranked by the FragmentScorer.
  3. Strategies are stateless: Each assembly cycle is independent. The pipeline runs all 10 components from scratch, and strategies receive the current budget and must respect it.
Progressive Refinement Within a Session

As an actor makes tool calls during a session, the conversation grows and the available budget shrinks. The ACMS handles this through a context refresh cycle that triggers re-assembly when the budget changes by more than 30% (configurable via context.budget.refresh_threshold).

Built-in Strategy Catalogue

The ACMS ships with several built-in strategies. The ARCE pipeline is one of them, not the only one.

Strategy: simple-keyword

Basic keyword/regex text search. Works with any backend, any resource type. No graph or vector required. This is the universal fallback. Quality score: 0.3.

Strategy: semantic-embedding

Vector similarity search. Finds semantically related content even without exact keyword matches. Requires a vector backend. Quality score: 0.6.

Strategy: breadth-depth-navigator

The graph-aware strategy that uses the depth/breadth projection system. Works with the UKO graph to provide structurally-aware context at any detail depth. Supports focus items, hop traversal, and detail depth gradients. This is the primary strategy for code-aware context. Quality score: 0.85.

Strategy: arce (Analyze, Retrieve, Contextualize, Execute)

The sophisticated multi-modal pipeline. Combines text, vector, and graph search with intent analysis and actor-type-aware patterns. This is the "kitchen sink" strategy that produces the highest quality results but requires all backends. Quality score: 0.95.

Actor-type-specific graph traversal patterns:

  • Strategy actors: Broader, shallower traversal. Emphasis on module boundaries and dependency structures. Prioritizes uko:Container and uko:Boundary nodes.
  • Execution actors: Narrower, deeper traversal. Emphasis on implementation details, function bodies, and test coverage. Prioritizes uko:Atom nodes.
  • Estimation actors: Focus on size metrics, complexity indicators, and historical data. Prioritizes temporal data.
Strategy: temporal-archaeology

Searches the cold tier for historical patterns. Useful for understanding "what changed last time we touched this area" or "what decisions were made about this module in the past." Requires graph + cold tier access. Quality score: 0.5.

Strategy: plan-decision-context

Retrieves context from parent and ancestor plan decisions. This is how child plans "remember" what their parent decided and why. Operates on the warm/cold tiers. Quality score: 0.7.

ACMS Extensibility

The ACMS-specific extension points (analyzers, backends, UKO vocabularies, strategies, and pipeline components) are documented in the Extensibility section below. See Extensibility > ACMS Extensions for the full details.

UKO Runtime Services

The UKO runtime is operationalized through three service classes:

  • UKOQueryInterface — Typed interface for ACMS context strategies to query UKO classification data. Provides classify_resource(uri) returning a ClassificationResult with layer (03), primary_type, source_resource_id, and relationships. Strategies use this to discover which ontology layer a resource belongs to and what relationships it has.

  • UKOInferenceEngine — Semantic analysis service that infers implicit relationships from UKO triples produced by domain analyzers. Infers three relationship types: uko:implicitSiblingOf (co-occurrence), uko:implicitContains (URI prefix containment), and uko:implicitDependsOn (URI reference in object values). Inferred triples are stored with confidence 0.7 to distinguish them from deterministic extractions.

  • UKOGraphPersistence — Serializes and restores UKO graph state via JSON or in-memory backends. Used for graph persistence across sessions and for test isolation.

The UKOIndexer.index_graph() method runs inference via UKOInferenceEngine and populates uko:layer triples for all four ontology layers during indexing.

Real-time Index Synchronization

The system maintains index freshness through immediate, proactive updates. The UKOIndexer produces UKO triples from resources using pluggable analyzers, and simultaneously indexes into text and vector backends:

class UKOIndexer:
    """Produces UKO triples from resources using pluggable analyzers."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">index_resource</span>(<span style="color: cyan;">self</span>, resource: <span style="color: cyan;">Resource</span>):
    <span style="color: #888;"># 1. Determine the best analyzer for this resource</span>
    analyzer = <span style="color: cyan;">self</span>.analyzers.get_for_resource(resource)
    <span style="color: #888;"># 2. Produce UKO triples using the most specific vocabulary</span>
    triples = analyzer.analyze(resource)
    <span style="color: #888;"># 3. Add provenance</span>
    <span style="color: magenta; font-weight: 600;">for</span> triple <span style="color: magenta; font-weight: 600;">in</span> triples:
        <span style="color: cyan;">self</span>._add_provenance(triple, resource)
    <span style="color: #888;"># 4. Store in graph backend</span>
    <span style="color: cyan;">self</span>.graph_backend.add_triples(triples)
    <span style="color: #888;"># 5. Also index into text and vector backends</span>
    <span style="color: cyan;">self</span>.text_indexer.index(resource, triples)
    <span style="color: cyan;">self</span>.vector_indexer.index(resource, triples)

Index Lifecycle
Stage Trigger Action Result
Resource added agents resource add / agents project link-resource Immediate full indexing All indices ready for instant search
Code changed File modification detected Immediate incremental update Indices reflect latest state
Resource removed agents resource remove / agents project unlink-resource Immediate cleanup Stale data removed, UKO nodes marked historical
Maintenance Scheduled Full reindex, consistency check Indices verified and optimized

When advanced features are unavailable, the system gracefully degrades. The pipeline's ConfidenceWeightedSelector (default StrategySelector component) automatically routes to simpler strategies:

  1. Try arce (requires all backends) -> if unavailable:
  2. Try breadth-depth-navigator (requires graph) -> if unavailable:
  3. Try semantic-embedding (requires vector) -> if unavailable:
  4. Fall back to simple-keyword (requires only text search / ripgrep)

ACMS Performance Characteristics

Assembly Latency
Operation Target Latency Notes
Resource scope resolution < 10ms In-memory set operations
Scoped backend view creation < 5ms Filter injection
Strategy can_handle check (all) < 20ms Simple capability checks
simple-keyword assembly < 100ms Text search
semantic-embedding assembly < 200ms Vector search
breadth-depth-navigator assembly < 500ms Graph traversal + materialization
arce assembly < 1s Multi-modal search + ranking
temporal-archaeology assembly < 2s Cold tier queries
Fragment fusion pipeline (phases 2-3) < 100ms Dedup + depth resolution + scoring + knapsack + ordering + preamble
Total initial assembly < 2s Parallel strategy execution
Incremental refresh < 500ms Re-compression only
Indexing and Query Performance
Metric Performance
Text indexing speed 10,000 files/minute
Vector indexing speed 1,000 files/minute (with GPU)
Graph indexing speed 5,000 files/minute
Text search query < 100ms for 1M files
Vector search query < 200ms for 10M embeddings
Graph traversal query < 500ms for 3-hop queries
Scalability Guarantees
  • 100K+ file projects: The scoping mechanism ensures strategies never see the full dataset. Scoped views filter at the backend level.
  • Deep plan hierarchies (10+ levels): Skeleton compression ensures parent context overhead stays bounded (10% per level, multiplicatively compressed).
  • Concurrent plans: Each plan gets independent ScopedBackendViews. No cross-plan interference.
  • Dynamic budgets: The knapsack algorithm is O(n) in the number of fragments. Re-compression is O(1) per fragment.
  • Maximum scale tested: 10M files, 1B+ triples, 100M+ embeddings.
Progressive Enhancement Path

Organizations can adopt ACMS features progressively. At each stage, existing resources are reindexed to take advantage of new capabilities:

Stage Features Requirements Benefit
Basic Text search, file watching ripgrep, sqlite Instant exact-match search
Semantic Vector embeddings, similarity search Embedding model, vector DB Instant semantic similarity search
Structural UKO graph, relationship queries Graph database, language analyzers Instant relationship queries
Intelligent ML-driven ranking, automated analysis GPU, training data Instant intelligent suggestions
Custom Domain-specific ontologies, custom analyzers Domain expertise Instant domain-aware intelligence

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.

Storage and Persistence

!!! adr "Architecture Decision" The hybrid storage model, database strategy, and migration approach are defined in ADR-019: Storage and Persistence.

This section defines the complete storage strategy for every persistent concept in CleverAgents, specifying the storage backend, data format, lifecycle management, and migration strategy for each entity type.

Storage Architecture Overview

CleverAgents uses a hybrid storage model combining a relational database for structured data, the filesystem for configuration files and artifacts, and specialized indexes for search operations. The storage layer is accessed exclusively through repository interfaces defined in the domain layer, ensuring that storage backends can be swapped without affecting application logic.

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 12
skinparam packageFontSize 13
skinparam packageFontStyle bold

package "Storage Architecture" as SA {
  package "Relational Database" as RDB #E3F2FD {
    component [Plans\nDecisions\nProjects\nResources\nSessions\nActors (db)\nInvariants\nChanges\nCheckpoint Metadata\nCorrection Attempts\nAudit Log] as Tables
  }

  package "Filesystem" as FS #C8E6C9 {
    component [Config YAML Files] as Cfg
    component [Sandbox Worktree Files] as Sbx
    component [Artifact Storage] as Art
  }

  package "Search Indexes" as IDX #F3E5F5 {
    component [Text (Tantivy)] as TI
    component [Vector (FAISS)] as VI
    component [Graph (Neo4j)] as GI
    component [Checkpoint Store\n(filesystem)] as CS
  }
}
@enduml

Entity Storage Map

The following table specifies where each CleverAgents concept is persisted, in both local and server modes:

Entity Local Storage Server Storage Format Identifier
Plans SQLite plans table Server PostgreSQL Relational rows ULID (plan_id)
Decisions SQLite decisions table Server PostgreSQL Relational rows + JSON context_snapshot ULID (decision_id)
Projects SQLite projects table Server PostgreSQL (for non-local/ namespaces) Relational rows Namespaced name
Resources SQLite resources table + resource_links table Server PostgreSQL Relational rows + DAG edges ULID (resource_id)
Resource Types SQLite resource_types table + YAML config files Server PostgreSQL + synced YAML Relational rows Namespaced name (built-ins unnamespaced)
Actions SQLite actions table + YAML config files Server PostgreSQL + synced YAML Relational rows Namespaced name
Actors SQLite actors table + YAML config files Server PostgreSQL + synced YAML Relational rows + file reference Namespaced name
Skills SQLite skills table + YAML config files Server PostgreSQL + synced YAML Relational rows + file reference Namespaced name
Tools SQLite tools table + YAML config files Server PostgreSQL + synced YAML Relational rows + file reference Namespaced name
Sessions SQLite sessions table + session_messages table Server PostgreSQL Relational rows ULID (session_id)
Invariants SQLite invariants table Server PostgreSQL Relational rows Auto-generated ID
Automation Profiles SQLite automation_profiles table + YAML config files Server PostgreSQL + synced YAML Relational rows Namespaced name (built-ins unnamespaced)
Changes / ChangeSets SQLite changes table Server PostgreSQL Relational rows ULID per change
Checkpoints Filesystem (<data-dir>/checkpoints/<plan_id>/) + SQLite checkpoint_metadata table Server object storage + PostgreSQL metadata Git refs or filesystem snapshots + metadata rows ULID (checkpoint_id)
Correction Attempts SQLite correction_attempts table + archived artifacts on filesystem Server PostgreSQL + object storage Relational rows + file archives ULID (correction_attempt_id)
Artifacts Filesystem (<data-dir>/artifacts/<plan_id>/) Server object storage (S3-compatible) Raw files (code, documents, images, etc.) Filesystem path relative to plan
Context Indexes (Text) Filesystem (<data-dir>/index/text/) Server-managed index cluster Tantivy index files or SQLite FTS5 Index directory per project
Context Indexes (Vector) Filesystem (<data-dir>/index/vector/) Server-managed FAISS/Qdrant cluster FAISS index files or Qdrant collections Index directory per project
Context Indexes (Graph) Neo4j database or rdflib files Server-managed Neo4j instance Graph triples Graph database per project
Configuration TOML file (<data-dir>/config.toml) N/A (server has own config) TOML Single file
Logs Filesystem (<data-dir>/logs/) Server-managed log aggregation Structured JSON (structlog) Rotated log files
Audit Logs SQLite audit_log table Server PostgreSQL Relational rows Auto-incrementing ID

Database Schema Design

The relational database follows a normalized design with foreign key constraints enforcing referential integrity. The schema is version-controlled through Alembic migrations.

Key design decisions:

  1. ULID primary keys: All time-series entities (plans, decisions, checkpoints, correction attempts) use ULID primary keys. This provides natural time-ordering without separate timestamp indexes, collision-free generation without coordination, and URL-safe string representation.

  2. JSON columns for semi-structured data: Fields like context_snapshot, alternatives_considered, artifacts_produced, and resource_bindings are stored as JSON text columns. This avoids over-normalization for data that is always read and written as a unit, while still being queryable via SQLite's json_extract() or PostgreSQL's jsonb operators.

  3. Soft delete pattern: Entities that support archival (actions, actors, skills) use a state column with values like available, archived. No rows are physically deleted except during explicit agents init reset or retention policy enforcement.

  4. Optimistic concurrency control: The updated_at timestamp column on mutable entities serves as a version check. Update operations include WHERE updated_at = <previous_value> to detect concurrent modifications. On conflict, the operation fails with an explicit error rather than silently overwriting.

Core tables (SQLite DDL):

-- Plans table: the fundamental unit of orchestration
CREATE TABLE plans (
    plan_id TEXT PRIMARY KEY,                    -- ULID
    parent_plan_id TEXT REFERENCES plans(plan_id),
    root_plan_id TEXT NOT NULL REFERENCES plans(plan_id),
    action_name TEXT NOT NULL,                    -- namespaced action name
    phase TEXT NOT NULL DEFAULT 'strategize',     -- action|strategize|execute|apply
    state TEXT NOT NULL DEFAULT 'queued',         -- queued|processing|errored|complete|cancelled
    attempt INTEGER NOT NULL DEFAULT 1,
    automation_profile_name TEXT NOT NULL,
    effective_profile_snapshot TEXT NOT NULL,      -- JSON: frozen profile at creation
    arguments TEXT,                                -- JSON: action arguments
    project_names TEXT NOT NULL,                   -- JSON array of project names
    strategy_actor_name TEXT,
    execution_actor_name TEXT,
    estimation_actor_name TEXT,
    invariant_actor_name TEXT,
    created_by TEXT,                               -- session ID or user identity
    created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
    updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
    completed_at TEXT,
    error_message TEXT,
    error_traceback TEXT,
    cost_estimate_usd REAL,
    cost_actual_usd REAL,
    token_count_input INTEGER DEFAULT 0,
    token_count_output INTEGER DEFAULT 0
);

CREATE INDEX idx_plans_parent ON plans(parent_plan_id); CREATE INDEX idx_plans_root ON plans(root_plan_id); CREATE INDEX idx_plans_phase_state ON plans(phase, state); CREATE INDEX idx_plans_action ON plans(action_name); CREATE INDEX idx_plans_created ON plans(created_at);

-- Decisions table: the decision tree CREATE TABLE decisions ( decision_id TEXT PRIMARY KEY, -- ULID plan_id TEXT NOT NULL REFERENCES plans(plan_id), parent_decision_id TEXT REFERENCES decisions(decision_id), decision_type TEXT NOT NULL, -- prompt_definition|invariant_enforced| -- strategy_choice|subplan_spawn| -- subplan_parallel_spawn|implementation_choice question TEXT NOT NULL, chosen_option TEXT NOT NULL, alternatives_considered TEXT, -- JSON array of alternative options confidence_score REAL, -- 0.0-1.0 rationale TEXT, context_snapshot TEXT NOT NULL, -- JSON: hot_context_hash, relevant_resources, -- actor_state_ref downstream_plan_ids TEXT, -- JSON array of child plan ULIDs artifacts_produced TEXT, -- JSON array of artifact references is_correction BOOLEAN DEFAULT FALSE, corrects_decision_id TEXT REFERENCES decisions(decision_id), superseded_by TEXT REFERENCES decisions(decision_id), sequence_number INTEGER NOT NULL, -- ordering within this plan created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')) );

CREATE INDEX idx_decisions_plan ON decisions(plan_id); CREATE INDEX idx_decisions_parent ON decisions(parent_decision_id); CREATE INDEX idx_decisions_type ON decisions(decision_type); CREATE INDEX idx_decisions_superseded ON decisions(superseded_by) WHERE superseded_by IS NOT NULL;

-- Resources table: the resource registry CREATE TABLE resources ( resource_id TEXT PRIMARY KEY, -- ULID name TEXT, -- namespaced name (NULL for auto-discovered children) resource_type_name TEXT NOT NULL, -- namespaced type name classification TEXT NOT NULL, -- physical|virtual description TEXT, properties TEXT, -- JSON: type-specific properties location TEXT, -- physical resources only content_hash TEXT, -- for equivalence tracking sandbox_strategy TEXT, -- override per resource created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')) );

CREATE UNIQUE INDEX idx_resources_name ON resources(name) WHERE name IS NOT NULL; CREATE INDEX idx_resources_type ON resources(resource_type_name); CREATE INDEX idx_resources_classification ON resources(classification);

-- Resource DAG: parent/child relationships CREATE TABLE resource_links ( parent_id TEXT NOT NULL REFERENCES resources(resource_id), child_id TEXT NOT NULL REFERENCES resources(resource_id), link_type TEXT DEFAULT 'contains', -- contains|references|derived_from created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')), PRIMARY KEY (parent_id, child_id) );

CREATE INDEX idx_resource_links_child ON resource_links(child_id);

-- Project-resource linkage CREATE TABLE project_resources ( project_name TEXT NOT NULL, resource_id TEXT NOT NULL REFERENCES resources(resource_id), read_only BOOLEAN DEFAULT FALSE, linked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')), PRIMARY KEY (project_name, resource_id) );

-- Checkpoint metadata (actual data on filesystem) CREATE TABLE checkpoint_metadata ( checkpoint_id TEXT PRIMARY KEY, -- ULID plan_id TEXT NOT NULL REFERENCES plans(plan_id), decision_id TEXT REFERENCES decisions(decision_id), checkpoint_type TEXT NOT NULL, -- pre_write|post_step|manual resource_id TEXT REFERENCES resources(resource_id), filesystem_path TEXT NOT NULL, -- relative path within checkpoint dir size_bytes INTEGER, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')) );

CREATE INDEX idx_checkpoints_plan ON checkpoint_metadata(plan_id);

-- Correction attempts CREATE TABLE correction_attempts ( correction_attempt_id TEXT PRIMARY KEY, -- ULID plan_id TEXT NOT NULL REFERENCES plans(plan_id), original_decision_id TEXT NOT NULL REFERENCES decisions(decision_id), new_decision_id TEXT REFERENCES decisions(decision_id), mode TEXT NOT NULL, -- revert|append guidance TEXT NOT NULL, archived_artifacts_path TEXT, -- filesystem path to archived originals state TEXT NOT NULL DEFAULT 'pending', -- pending|executing|complete|failed created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')), completed_at TEXT );

CREATE INDEX idx_corrections_plan ON correction_attempts(plan_id);

-- Audit log for apply operations CREATE TABLE audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_type TEXT NOT NULL, -- plan_applied|plan_cancelled|resource_modified| -- correction_applied|config_changed plan_id TEXT, project_name TEXT, actor_name TEXT, user_identity TEXT, details TEXT NOT NULL, -- JSON: event-specific details created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')) );

CREATE INDEX idx_audit_event ON audit_log(event_type); CREATE INDEX idx_audit_plan ON audit_log(plan_id) WHERE plan_id IS NOT NULL; CREATE INDEX idx_audit_created ON audit_log(created_at);

Filesystem Layout

The data directory follows a deterministic layout. All paths are relative to core.data-dir (default: ~/.cleveragents):


~/.cleveragents/
├── config.toml                    # Global configuration
├── cleveragents.db                # SQLite database (WAL mode)
├── cleveragents.db-wal            # WAL file (auto-managed by SQLite)
├── cleveragents.db-shm            # Shared memory file (auto-managed)
├── logs/
│   ├── cleveragents.log           # Current log file (structured JSON)
│   ├── cleveragents.log.1         # Rotated log files
│   └── ...
├── cache/
│   ├── models/                    # Cached model artifacts
│   ├── tools/                     # Cached tool downloads
│   └── templates/                 # Compiled Jinja2 templates
├── backups/
│   ├── 2026-02-08T12-30-00/       # Timestamped backup snapshots
│   └── ...
├── checkpoints/
│   ├── 01HXR1C1D2E3F4G5H6I7.../  # Per-plan checkpoint directories
│   │   ├── chk_01HXR1E1.tar.gz   # Compressed checkpoint snapshots
│   │   └── ...
│   └── ...
├── artifacts/
│   ├── 01HXR1C1D2E3F4G5H6I7.../  # Per-plan artifact directories
│   │   ├── src/routes/health.py   # Generated/modified artifacts
│   │   └── ...
│   └── ...
├── index/
│   ├── text/
│   │   ├── local_api-service/     # Per-project Tantivy indexes
│   │   └── ...
│   ├── vector/
│   │   ├── local_api-service/     # Per-project FAISS indexes
│   │   └── ...
│   └── graph/
│       └── ...                     # rdflib stores or Neo4j connection info
├── sessions/
│   └── ...                         # Session state files (if needed beyond DB)
└── contexts/
    └── ...                         # Exported/cached context snapshots

Data Lifecycle and Retention

Data Type Retention Policy Cleanup Trigger
Plans (active) Indefinite until the plan reaches applied or cancelled state Manual deletion
Plans (terminal) Governed by core.backup.retention-days for archives Automatic cleanup daemon
Decisions Retained as long as parent plan exists Cascade delete with plan
Checkpoints sandbox.checkpoint.max-per-plan per plan; oldest pruned first (keeping first + most recent) After apply or on retention limit
Correction archives core.backup.retention-days after plan reaches applied state Automatic cleanup daemon
Logs core.log.retention-days Daily rotation + age-based cleanup
Backups core.backup.retention-days Age-based cleanup
Cache No automatic retention; manual cleanup via agents diagnostics --cleanup User-initiated
Search indexes Rebuilt on resource add/remove; no explicit retention Re-index on change
Audit logs Indefinite (critical for compliance) Manual archival only

Migration Strategy

Database schema changes are managed through Alembic migrations with the following rules:

  1. Forward-only in production: Downgrade scripts are provided but only used in development. Production deployments only move forward.
  2. Auto-apply on startup: When CLEVERAGENTS_AUTO_APPLY_MIGRATIONS is set (default in development), migrations run automatically on first database access. In production, migrations are triggered by agents init or explicit upgrade commands.
  3. Backward compatibility window: Each migration maintains backward compatibility with the previous schema version for at least one minor release cycle, allowing rolling upgrades in server mode.
  4. Data migration separation: Schema DDL changes and data transformations are kept in separate migration steps to enable rollback of data changes independently.

Observability

!!! adr "Architecture Decision" The observability strategy, structured logging, metrics, and distributed tracing are defined in ADR-025: Observability and Logging.

CleverAgents implements a comprehensive observability strategy across three pillars — logging, metrics, and tracing — designed to provide full visibility into plan execution, actor behavior, tool invocations, and resource access. Every significant operation in the system is observable, enabling debugging of complex multi-plan hierarchies and performance optimization.

Structured Logging

All logging uses structlog with JSON output format. Every log entry includes contextual fields that enable correlation across plan hierarchies:

import structlog

logger = structlog.get_logger()

# Every log entry automatically includes bound context logger = logger.bind( plan_id="01HXR1C1D2E3F4G5H6I7J8K9L0", phase="execute", actor_name="anthropic/claude-3.5-sonnet", session_id="01HXM2A6K1P2E9Q9D4GQ7J4S7Z", )

# Example: tool invocation logging logger.info( "tool_invoked", tool_name="local/write-file", skill_name="local/file-ops", resource_id="01HXR1A1B2C3D4E5F6G7H8J9K0", resource_type="git-checkout", input_schema_hash="sha256:a1b2c3...", duration_ms=142, writes=True, checkpoint_created=True, )

Log levels and their semantics:

Level Usage Examples
DEBUG Internal state transitions, context building details, LLM prompt construction "Building hot context: 12 files, 8,420 tokens", "Decision confidence computed: 0.87"
INFO Significant lifecycle events, phase transitions, tool completions "Plan entered execute phase", "Tool write-file completed in 142ms", "Validation passed: pytest"
WARNING Degraded operations, approaching limits, non-fatal issues "Budget at 82% ($4.10/$5.00)", "Checkpoint pruned: exceeded max-per-plan", "Provider fallback: openai -> anthropic"
ERROR Operation failures, invariant violations, unrecoverable tool errors "Validation failed: pytest exited 1", "Invariant violated: API compatibility", "LLM provider error: rate limited"

Log correlation: Every log entry within a plan execution context includes plan_id, root_plan_id, parent_plan_id, phase, state, and attempt. This enables tracing a single decision through the entire plan hierarchy using standard log querying tools (e.g., jq '.plan_id == "01HXR1C1..."').

Event System

CleverAgents emits domain events for every significant state change. Events flow through two channels:

  1. RxPY Reactive Stream: Real-time event stream for in-process subscribers (TUI updates, progress bars, streaming output). Uses Subject for fan-out distribution.
  2. Persistent Event Log: All events are written to the audit_log table for post-hoc analysis and compliance.

Event taxonomy:

from enum import Enum
from pydantic import BaseModel
from datetime import datetime

class EventType(StrEnum): # Plan lifecycle events PLAN_CREATED = "plan.created" PLAN_PHASE_CHANGED = "plan.phase_changed" PLAN_STATE_CHANGED = "plan.state_changed" PLAN_APPLIED = "plan.applied" PLAN_CANCELLED = "plan.cancelled" PLAN_ERRORED = "plan.errored" PLAN_ESTIMATION_COMPLETE = "plan.estimation_complete" # emitted after Strategize when estimation_actor is set

<span style="color: #888;"># Decision events</span>
DECISION_CREATED = <span style="color: #66cc66;">"decision.created"</span>
DECISION_APPROVED = <span style="color: #66cc66;">"decision.approved"</span>
DECISION_CORRECTED = <span style="color: #66cc66;">"decision.corrected"</span>
DECISION_SUPERSEDED = <span style="color: #66cc66;">"decision.superseded"</span>

<span style="color: #888;"># Invariant events</span>
INVARIANT_RECONCILED = <span style="color: #66cc66;">"invariant.reconciled"</span>
INVARIANT_VIOLATED = <span style="color: #66cc66;">"invariant.violated"</span>
INVARIANT_ENFORCED = <span style="color: #66cc66;">"invariant.enforced"</span>

<span style="color: #888;"># Actor events</span>
ACTOR_INVOKED = <span style="color: #66cc66;">"actor.invoked"</span>
ACTOR_COMPLETED = <span style="color: #66cc66;">"actor.completed"</span>
ACTOR_ERRORED = <span style="color: #66cc66;">"actor.errored"</span>
ACTOR_ESCALATED = <span style="color: #66cc66;">"actor.escalated"</span>       <span style="color: #888;"># confidence below threshold</span>

<span style="color: #888;"># Tool events</span>
TOOL_INVOKED = <span style="color: #66cc66;">"tool.invoked"</span>
TOOL_COMPLETED = <span style="color: #66cc66;">"tool.completed"</span>
TOOL_ERRORED = <span style="color: #66cc66;">"tool.errored"</span>
TOOL_RETRIED = <span style="color: #66cc66;">"tool.retried"</span>

<span style="color: #888;"># Resource events</span>
RESOURCE_ACCESSED = <span style="color: #66cc66;">"resource.accessed"</span>
RESOURCE_MODIFIED = <span style="color: #66cc66;">"resource.modified"</span>
RESOURCE_INDEXED = <span style="color: #66cc66;">"resource.indexed"</span>

<span style="color: #888;"># Correction events</span>
CORRECTION_APPLIED = <span style="color: #66cc66;">"correction.applied"</span>

<span style="color: #888;"># Configuration events</span>
CONFIG_CHANGED = <span style="color: #66cc66;">"config.changed"</span>

<span style="color: #888;"># Entity lifecycle events</span>
ENTITY_DELETED = <span style="color: #66cc66;">"entity.deleted"</span>

<span style="color: #888;"># Authentication events (server-mode only)</span>
AUTH_SUCCESS = <span style="color: #66cc66;">"auth.success"</span>
AUTH_FAILURE = <span style="color: #66cc66;">"auth.failure"</span>

<span style="color: #888;"># Sandbox events</span>
SANDBOX_CREATED = <span style="color: #66cc66;">"sandbox.created"</span>
SANDBOX_COMMITTED = <span style="color: #66cc66;">"sandbox.committed"</span>
SANDBOX_ROLLED_BACK = <span style="color: #66cc66;">"sandbox.rolled_back"</span>
CHECKPOINT_CREATED = <span style="color: #66cc66;">"checkpoint.created"</span>
CHECKPOINT_RESTORED = <span style="color: #66cc66;">"checkpoint.restored"</span>

<span style="color: #888;"># Context events</span>
CONTEXT_BUILT = <span style="color: #66cc66;">"context.built"</span>
CONTEXT_QUERY_EXECUTED = <span style="color: #66cc66;">"context.query_executed"</span>

<span style="color: #888;"># Context tier lifecycle events</span>
TIER_PROMOTED = <span style="color: #66cc66;">"tier.promoted"</span>
TIER_DEMOTED = <span style="color: #66cc66;">"tier.demoted"</span>
TIER_EVICTED = <span style="color: #66cc66;">"tier.evicted"</span>

<span style="color: #888;"># Validation events</span>
VALIDATION_STARTED = <span style="color: #66cc66;">"validation.started"</span>
VALIDATION_PASSED = <span style="color: #66cc66;">"validation.passed"</span>
VALIDATION_FAILED = <span style="color: #66cc66;">"validation.failed"</span>
VALIDATION_FIX_ATTEMPTED = <span style="color: #66cc66;">"validation.fix_attempted"</span>
VALIDATION_FIX_SUCCEEDED = <span style="color: #66cc66;">"validation.fix_succeeded"</span>
VALIDATION_FIX_EXHAUSTED = <span style="color: #66cc66;">"validation.fix_exhausted"</span>

<span style="color: #888;"># Session events</span>
SESSION_CREATED = <span style="color: #66cc66;">"session.created"</span>
SESSION_MESSAGE_SENT = <span style="color: #66cc66;">"session.message_sent"</span>

<span style="color: #888;"># Cost events</span>
BUDGET_WARNING = <span style="color: #66cc66;">"budget.warning"</span>
BUDGET_EXCEEDED = <span style="color: #66cc66;">"budget.exceeded"</span>

class DomainEvent(BaseModel): """Base event model emitted by all domain operations.""" event_type: EventType timestamp: datetime # UTC; defaults to now(UTC) correlation_id: str # ULID linking related events in one request chain plan_id: str | None = None root_plan_id: str | None = None session_id: str | None = None actor_name: str | None = None project_name: str | None = None user_identity: str | None = None # Authenticated user; None in local (unauthenticated) mode details: dict # Event-type-specific payload

Event emission pattern: Domain services emit events through an injected EventBus interface. The infrastructure layer provides two implementations:

from typing import Protocol
import rx
from rx.subject import Subject

class EventBus(Protocol): def emit(self, event: DomainEvent) -> None: ... def subscribe(self, event_type: EventType, handler: Callable) -> None: ...

class ReactiveEventBus: """In-process event bus using RxPY for real-time distribution.""" def init(self): self._subject = Subject() self._subscriptions: dict[EventType, list[Callable]] = {}

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">emit</span>(<span style="color: cyan;">self</span>, event: DomainEvent) -> <span style="color: cyan;">None</span>:
    <span style="color: #888;"># Push to reactive stream for real-time subscribers</span>
    <span style="color: cyan;">self</span>._subject.on_next(event)
    <span style="color: #888;"># Persist to audit log</span>
    <span style="color: cyan;">self</span>._persist_audit(event)
    <span style="color: #888;"># Dispatch to type-specific handlers</span>
    <span style="color: magenta; font-weight: 600;">for</span> handler <span style="color: magenta; font-weight: 600;">in</span> <span style="color: cyan;">self</span>._subscriptions.get(event.event_type, []):
        handler(event)

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">subscribe</span>(<span style="color: cyan;">self</span>, event_type: <span style="color: cyan;">EventType</span>, handler: <span style="color: cyan;">Callable</span>) -> <span style="color: cyan;">None</span>:
    <span style="color: cyan;">self</span>._subscriptions.setdefault(event_type, []).append(handler)

<span style="color: cyan;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">stream</span>(<span style="color: cyan;">self</span>) -> rx.Observable:
    <span style="color: #888;">"""Raw observable stream for advanced RxPY operators."""</span>
    <span style="color: magenta; font-weight: 600;">return</span> <span style="color: cyan;">self</span>._subject

LLM Call Tracing

Every LLM invocation is traced with full context for debugging and cost tracking:

# Trace record for each LLM call
class LLMTrace(BaseModel):
    trace_id: str                     # ULID
    plan_id: str
    decision_id: str | None
    actor_name: str
    provider: str                     # openai, anthropic, google, etc.
    model: str                        # gpt-4o, claude-3.5-sonnet, etc.
    prompt_tokens: int
    completion_tokens: int
    total_tokens: int
    cost_usd: float
    latency_ms: int
    temperature: float
    tool_calls: list[str]             # tool names invoked by the LLM
    context_hash: str                 # hash of the context window contents
    context_refs: list[str]           # resource IDs referenced in context
    streaming: bool
    retry_count: int
    error: str | None
    timestamp: datetime

LangSmith integration: When CLEVERAGENTS_LANGSMITH_ENABLED is true, all LLM traces are additionally forwarded to LangSmith for visualization, comparison, and cost analysis. The integration maps CleverAgents plan IDs to LangSmith run tags, enabling filtering by plan hierarchy in the LangSmith UI.

Metrics Collection

CleverAgents collects operational metrics for performance monitoring and capacity planning. Metrics are emitted as structured log entries (for local mode) and optionally exported to Prometheus (for server mode):

Metric Type Description
plan.duration_seconds Histogram Total wall-clock time per plan, labeled by phase
plan.cost_usd Counter Cumulative API cost per plan
plan.decisions_count Counter Number of decisions per plan
plan.child_plans_count Counter Number of child plans spawned
actor.invocation_duration_ms Histogram Per-actor invocation latency
actor.token_usage Counter Token counts by provider and model
tool.invocation_duration_ms Histogram Per-tool invocation latency
tool.error_rate Counter Tool invocation failures by tool name
context.build_duration_ms Histogram Context building time by tier (hot/warm/cold)
context.tokens_used Gauge Current token usage in hot context
index.query_duration_ms Histogram Index query latency by backend (text/vector/graph)
sandbox.operation_duration_ms Histogram Sandbox create/commit/rollback time
validation.duration_seconds Histogram Per-validation tool invocation execution time
validation.pass_rate Counter Validation pass/fail counts (from passed field in return JSON)

Diagnostic Dashboard

The agents diagnostics command provides a real-time health check combining all observability data:

  1. System health: Config file readability, database accessibility, disk space, provider API key validation.
  2. Index health: Text index status, vector index dimensionality and count, graph store connectivity.
  3. Active plan overview: Running plans, queued plans, resource utilization.
  4. Cost summary: Current session cost, budget utilization, provider-level breakdown.
  5. Performance summary: Average plan duration, tool call latency percentiles, context build times.

Security Model

!!! adr "Architecture Decision" Sandbox isolation is covered in ADR-015: Sandbox and Checkpoint. The server-mode security model is defined in ADR-023: Server Mode.

CleverAgents implements a defense-in-depth security model addressing five concerns: sandbox isolation, access control, prompt injection mitigation, secret management, and audit logging. The security model differs between local mode (single-user, trusted environment) and server mode (multi-user, potentially untrusted environment).

Sandbox Isolation

The sandbox is the primary safety mechanism preventing untested changes from reaching production resources. Every plan's Execute phase runs within an isolated sandbox unless explicitly disabled by the automation profile (require_sandbox: false).

Sandbox strategies and their security properties:

Strategy Isolation Level Resource Type Mechanism Rollback
git_worktree Process-level filesystem isolation Git repositories Creates a separate git worktree on a plan-specific branch. Changes are confined to the worktree directory. Merge back to main requires explicit apply. git checkout -- . or worktree deletion
copy_on_write Process-level filesystem isolation Filesystems with native CoW support (e.g., BTRFS, ZFS) Leverages the filesystem's native copy-on-write capability to create a lightweight snapshot. The filesystem preserves original blocks when edits occur, requiring no explicit copy. Only available on CoW-capable filesystems. Restore from snapshot
filesystem_copy Process-level filesystem isolation All writable filesystems Creates an explicit full copy of the resource directory (using cp or equivalent). Works on any writable filesystem regardless of CoW support, at the cost of duplicating data upfront. Original files are never modified during execution. Delete the copy directory
overlay Process-level filesystem isolation Filesystems supporting overlay mounts Uses an overlay filesystem (e.g., OverlayFS) to layer changes on top of the original. Writes go to the upper layer; the lower layer remains untouched. Remove the overlay mount
transaction_rollback Database transaction isolation Databases Opens a database transaction with SERIALIZABLE isolation level. All changes are staged within the transaction. Commit on apply; rollback on cancel/error. ROLLBACK
none No isolation Any Changes are applied directly. Requires require_sandbox: false in the automation profile. Only appropriate when external safety mechanisms exist. No automatic rollback

Sandbox security invariants:

  1. No cross-sandbox access: Each plan's sandbox is isolated. Plan A cannot read or write Plan B's sandbox contents. The sandbox path is derived deterministically from the plan ULID, preventing path traversal.
  2. Read-only source guarantee during Execute: The original project resources are never modified during Execute. All tool write operations target the sandbox copy.
  3. Atomic apply: The Apply phase is an atomic operation — either all sandbox changes are committed to the real resources, or none are. For git-based resources, this uses git merge. For database resources, this uses transaction commit.
  4. Sandbox cleanup: Sandbox directories are cleaned up according to sandbox.cleanup policy. Sensitive data (API responses, LLM outputs) is purged from the sandbox on cleanup.

Prompt Injection Mitigation

In server mode, where user-provided content may flow into LLM prompts, CleverAgents implements several mitigations:

  1. Input sanitization: User-provided text in action arguments, invariant text, and session prompts is sanitized before inclusion in LLM prompts. HTML entities, control characters, and known injection patterns are escaped or rejected.

  2. Prompt boundary markers: System prompts and user content are separated by clear boundary markers that the LLM is instructed to recognize. The system prompt explicitly states that content between [USER_CONTENT_START] and [USER_CONTENT_END] markers is user-provided and should not be treated as system instructions.

  3. Output validation: LLM outputs that are used as tool invocations are validated against the tool's JSON Schema before execution. This prevents the LLM from being tricked into invoking tools with malicious parameters.

  4. Tool capability restrictions: Tools declare their capabilities (read_only, writes, checkpointable, side_effects). The execution engine enforces these declarations — a tool declared as read_only cannot invoke write operations even if the LLM requests it.

  5. Unsafe tool gating: Tools marked as unsafe (e.g., arbitrary shell execution, network access) are blocked unless the automation profile explicitly sets allow_unsafe_tools: true. In server mode, only administrators can create automation profiles that allow unsafe tools.

Secret Management

Secrets (API keys, database credentials, authentication tokens) are managed through a layered approach:

  1. Environment variables (preferred): Provider API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) and the server token (CLEVERAGENTS_SERVER_TOKEN) are read from environment variables. This is the recommended approach for both local and CI environments.

  2. Configuration file (fallback): API keys can be stored in config.toml under the provider.* section. The config file should have restrictive permissions (chmod 600). The agents diagnostics command warns if config file permissions are too permissive.

  3. Secret masking in logs: All log output and CLI display automatically masks values that match known secret patterns (keys starting with sk-, sk-ant-, tok_, etc.). Masked values are replaced with ***REDACTED***. The masking is applied at the structlog processor level, ensuring no secret leaks through any log path.

  4. Secret masking in LLM context: Before constructing LLM prompts, the context builder scans for patterns matching known secret formats and replaces them with [REDACTED]. This prevents accidental exposure of secrets in LLM training data (for providers that use customer data for training, which CleverAgents discourages).

  5. Server mode credential isolation: In server mode, each user's API keys are stored encrypted in the server database using AES-256-GCM with a per-user key derived from the server's master secret. Keys are decrypted only at the moment of LLM invocation and are never transmitted to the client CLI.

Exception Handling and Error Classification

All exceptions surfacing through the CLI are processed by the cleveragents.core.error_handling module, which provides three guarantees: classification, redaction, and safe wrapping.

Error classification: Every exception is mapped to an HTTP-like numeric error code via the exception class hierarchy. The mapping uses the exception's MRO (Method Resolution Order) to find the most specific match:

Range Category Examples
400-range Client / input errors ValidationError → 422, ResourceNotFoundError → 404, AuthenticationError → 401, RateLimitError → 429
500-range Server / infrastructure errors DatabaseError → 522, NetworkError → 524, ProviderError → 520, ConfigurationError → 525

Unknown exceptions (not inheriting from CleverAgentsError) default to code 500 (INTERNAL).

Secret redaction in error output: Both the exception message and its details dict are redacted before display. Redaction delegates to cleveragents.shared.redaction (the single redaction implementation shared with structlog processors and CLI output). This ensures:

  • Key names matching sensitive substrings (api_key, password, secret, token, credential, auth, etc.) have values fully replaced with ***REDACTED***.
  • String values are scanned for known secret patterns (OpenAI keys, Anthropic keys, JWT tokens, GitHub PATs, GitLab PATs, Bearer tokens).
  • Nested dicts and lists are recursively redacted.
  • Error details always enforce redaction (show_secrets=False) regardless of the global show_secrets flag.

Safe wrapping of unexpected exceptions: The wrap_unexpected function converts bare Exception instances into CleverAgentsError with a generic, user-safe message ("An unexpected error occurred"). Internal stack details are captured in the error's details dict for diagnostics but are not shown to the user. If the exception is already a CleverAgentsError, it is returned unchanged with optional context merged into a copy of its details.

CLI integration: The top-level error handlers in cli/main.py use classify_error for CleverAgentsError exceptions and wrap_unexpected + classify_error for bare exceptions. CLI output includes the error code and category name for consistent, machine-parseable error reporting:

Error [422] VALIDATION_FAILED: name is required
Error [500] INTERNAL: An unexpected error occurred

See docs/reference/error_handling.md for the complete API reference and exception hierarchy.

Audit Logging

All security-relevant operations are recorded in the audit_log table with the following events:

Event Type Trigger Details Captured
plan_applied agents plan apply Plan ID, project names, files changed, lines added/removed, resources modified, apply duration, user identity
plan_cancelled agents plan cancel Plan ID, reason, project names, cancelled phase, cancelled processing state, last completed step, subplan count, sandbox refs, changeset ID, resources pending cleanup
resource_modified Tool write operations during Execute Resource ID, modification type, sandbox path, tool name
correction_applied agents plan correct Correction attempt ID, original decision ID, mode, guidance
config_changed agents config set Key, old value (masked if secret), new value (masked if secret), user identity
entity_deleted agents <entity> delete/remove Entity type, entity name, cascade effects
session_created agents session create Session ID, actor name, user identity
auth_success Server mode login User identity, IP address, token prefix
auth_failure Server mode failed login Attempted identity, IP address, failure reason

Audit log retention: Audit logs are never automatically deleted. In server mode, audit logs can be exported for compliance archival using server administration commands. In local mode, audit logs are preserved in the SQLite database indefinitely and are included in backup snapshots.

Extensibility

!!! adr "Architecture Decision" The extensibility model and plugin architecture are grounded in the layered design defined in ADR-001: Layered Architecture and the configuration system in ADR-024: Configuration System.

CleverAgents is designed as an extensible platform where every major subsystem supports custom implementations. The extensibility model follows the Open/Closed Principle — the system is open for extension through well-defined interfaces, but closed for modification of core behavior.

Plugin Architecture Overview

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 12
skinparam packageFontSize 13
skinparam packageFontStyle bold
skinparam componentFontSize 11

package "Extension Points" as EP {
  package "User-Facing Extensions" as UFE #C8E6C9 {
    component [Custom Tools\n(YAML/Python)] as CT
    component [Custom Skills\n(YAML)] as CS
    component [Custom Actors\n(YAML/Graph)] as CA
  }

  package "Integration Extensions" as IE #E3F2FD {
    component [MCP Servers\n(stdio/SSE)] as MCP
    component [Agent Skills\nStandard] as ASS
    component [Custom LLM\nProviders] as CLP
  }

  package "Core Registries" as CR #F3E5F5 {
    component [Tool Registry\nSkill Registry\nActor Registry\nProvider Registry] as REG
  }

  package "Infrastructure Extensions" as INF #FFF9C4 {
    component [Custom Resource\nTypes (YAML)] as CRT
    component [Custom Sandbox\nStrategies (Python)] as CSS
    component [Custom Index\nBackends] as CIB
  }

  package "ACMS Pipeline Extensions" as ACMS #E8EAF6 {
    component [Custom UKO\nAnalyzers / Vocabularies] as UKO
    component [Custom Context\nStrategies] as CCS
    component [Custom Pipeline\nComponents (10 slots)] as CPC
  }

  CT -down-> REG
  CS -down-> REG
  CA -down-> REG
  MCP -down-> REG
  ASS -down-> REG
  CLP -down-> REG
  REG -down-> CRT
  REG -down-> CSS
  REG -down-> CIB
  REG -down-> UKO
  REG -down-> CCS
  REG -down-> CPC
}
@enduml

Tool Extensibility

Tools are the atomic unit of execution and the primary extension point. There are five ways to add tools:

  1. Custom Python tools: Define a tool with inline Python code in a YAML configuration file. The code runs in a sandboxed execution context with access to the plan context, resource bindings, and checkpoint API.

    # File: tools/my-custom-tool.yaml
    tool:
      name: local/analyze-complexity
      description: "Compute cyclomatic complexity for Python files"
      source: custom
    

    input_schema: type: object properties: file_path: { type: string } required: [file_path]

    output_schema: type: object properties: complexity: { type: integer } functions: { type: array }

    capability: writes: false checkpointable: false

    resource_slots: - name: source type: git-checkout binding: contextual

    code: | import ast content = ctx.resources["source"].read(params["file_path"]) tree = ast.parse(content) # ... complexity analysis ... return {"complexity": total, "functions": results}

  2. MCP server tools: Any MCP-compliant server can expose tools to CleverAgents. Skills reference MCP servers by their transport configuration:

    # File: skills/kubernetes-ops.yaml
    skill:
      name: local/kubernetes-ops
      description: "Kubernetes cluster management tools"
      mcp_servers:
        - transport: stdio
          command: npx
          args: ["-y", "@anthropic/mcp-kubernetes"]
    
  3. Agent Skills Standard tools: Tools organized in standard folder structures are auto-discovered and registered:

    skill:
      name: local/project-tools
      agent_skills_dirs:
        - ./agent-skills/
    
  4. Built-in tools: Core file operations (read_file, write_file, edit_file, delete_file, move_file, list_files, search_files), plan operations (create-subplan), and system operations are provided as built-in tools grouped into built-in skills.

  5. Anonymous inline tools: Tools can be defined inline within actor graph nodes or skill definitions for one-off operations that don't need global registration.

Tool lifecycle hooks: Every tool follows a four-phase lifecycle — discover (find available capabilities), activate (prepare for use, acquire connections), execute (perform the operation), deactivate (release resources). The tool registry manages these lifecycle transitions.

Skill Extensibility

Skills compose tools into capability collections. Skills support:

  • Tool aggregation: Reference named tools from the Tool Registry
  • Inline anonymous tools: Define tools directly in the skill YAML
  • Skill inclusion: Include other skills by reference (composability)
  • MCP server aggregation: Expose tools from one or more MCP servers
  • Agent Skills Standard: Expose tools from Agent Skills Standard directories

Actor Extensibility

Actors can be extended through:

  1. YAML-defined agents: Single LLM actors with custom system prompts, temperature settings, tool bindings, and capability constraints.
  2. YAML-defined graphs: LangGraph topologies with multiple actors and tool nodes connected by edges, conditional routing, and parallel execution groups.
  3. Provider extension: New LLM providers can be added by implementing the AIProviderInterface protocol and registering with the ProviderRegistry. The registry uses auto-discovery to detect installed langchain-* packages.
from typing import Protocol

class AIProviderInterface(Protocol): """Protocol for LLM provider implementations."""

<span style="color: cyan;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">provider_name</span>(<span style="color: cyan;">self</span>) -> <span style="color: cyan;">str</span>: ...

<span style="color: cyan;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">capabilities</span>(<span style="color: cyan;">self</span>) -> ProviderCapabilities: ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">create_chat_model</span>(
    <span style="color: cyan;">self</span>,
    model: <span style="color: cyan;">str</span>,
    temperature: <span style="color: cyan;">float</span> = <span style="color: yellow;">0.7</span>,
    **kwargs,
) -> BaseChatModel: ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">create_embedding_model</span>(
    <span style="color: cyan;">self</span>,
    model: <span style="color: cyan;">str</span>,
    **kwargs,
) -> BaseEmbeddings: ...

Custom Resource Types

New resource types extend CleverAgents to manage any kind of resource:

# File: resource-types/docker-registry.yaml
resource_type:
  name: local/docker-registry
  description: "Docker container registry"
  classification: physical
  user_addable: true

cli_args: - name: registry_url type: string required: true - name: repository type: string required: true - name: tag type: string default: "latest"

sandbox_strategy: none # Docker images are immutable handler: docker_registry_handler

child_types: - type: local/docker-image relationship: contains auto_discover: true

auto_discovery: enabled: true strategy: list_tags

Custom Index Backends

The indexing layer is abstracted behind three interfaces (text, vector, graph). Custom implementations can be registered for each:

class TextIndexBackend(Protocol):
    """Interface for full-text search backends."""
    def index_document(self, project: str, doc_id: str, content: str, metadata: dict) -> None: ...
    def search(self, project: str, query: str, limit: int) -> list[SearchResult]: ...
    def remove_document(self, project: str, doc_id: str) -> None: ...
    def rebuild_index(self, project: str) -> None: ...

class VectorIndexBackend(Protocol): """Interface for vector similarity search backends.""" def index_embedding(self, project: str, doc_id: str, embedding: list[float], metadata: dict) -> None: ... def search_similar(self, project: str, query_embedding: list[float], limit: int, min_relevance: float) -> list[VectorResult]: ... def remove_embedding(self, project: str, doc_id: str) -> None: ...

class GraphIndexBackend(Protocol): """Interface for knowledge graph backends.""" def add_triple(self, project: str, subject: str, predicate: str, obj: str) -> None: ... def query(self, project: str, sparql: str) -> list[dict]: ... def remove_triples(self, project: str, subject: str | None, predicate: str | None, obj: str | None) -> None: ...

New backends are registered via configuration:

# config.toml
[index.text]
backend = "custom"
custom_module = "my_extensions.elasticsearch_backend"
custom_class = "ElasticsearchTextIndex"

[index.text.custom_options] hosts = ["http://localhost:9200"] index_prefix = "cleveragents"

Custom Sandbox Strategies

The sandbox layer supports custom isolation strategies for specialized resource types:

class SandboxStrategy(Protocol):
    """Interface for sandbox isolation strategies."""
    def create(self, plan_id: str, resource: Resource) -> SandboxRef: ...
    def read(self, ref: SandboxRef, path: str) -> bytes: ...
    def write(self, ref: SandboxRef, path: str, content: bytes) -> Change: ...
    def diff(self, ref: SandboxRef) -> DiffView: ...
    def commit(self, ref: SandboxRef) -> None: ...
    def rollback(self, ref: SandboxRef) -> None: ...
    def checkpoint(self, ref: SandboxRef, checkpoint_id: str) -> None: ...
    def restore_checkpoint(self, ref: SandboxRef, checkpoint_id: str) -> None: ...
    def cleanup(self, ref: SandboxRef) -> None: ...

Custom strategies are mapped to resource types via the resource type configuration's sandbox_strategy field, or globally via sandbox.strategy config.

ACMS Extensions

The ACMS provides five categories of extension points: UKO analyzers, index backends, context strategies, UKO vocabularies, and Context Assembly Pipeline components.

Domain and Language-Specific Analyzers

Analyzers are pluggable components that parse resources and produce UKO triples. They are organized by domain (code, documents, data, infrastructure) and then by specific technology within each domain:

PluginSystem:
  analyzers:
    # --- Source Code Analyzers (Layer 1: uko-code + Layer 2/3) ---
    python:
      class: "PythonAnalyzer"
      features: [AST parsing, 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_dsl:
      class: "CustomDSLAnalyzer"
      config:
        grammar: "path/to/grammar.peg"
        semantic_rules: "path/to/rules.yaml"
<span style="color: #888;"># --- Document Analyzers (Layer 1: uko-doc) ---</span>
<span style="color: cyan; font-weight: 600;">markdown</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"MarkdownAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Heading hierarchy, Link extraction, Code block detection, Topic inference, Cross-reference resolution</span>]
<span style="color: cyan; font-weight: 600;">restructuredtext</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"ReStructuredTextAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Directive parsing, Role resolution, Cross-reference resolution, Index extraction</span>]
<span style="color: cyan; font-weight: 600;">html</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"HTMLDocumentAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Semantic HTML parsing, Heading extraction, Link graph, Readability scoring</span>]

<span style="color: #888;"># --- Data Schema Analyzers (Layer 1: uko-data) ---</span>
<span style="color: cyan; font-weight: 600;">postgresql</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"PostgreSQLAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Schema introspection, DDL extraction, Foreign key graph, View dependency analysis, Stored procedure parsing, Statistics collection</span>]
<span style="color: cyan; font-weight: 600;">mysql</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"MySQLAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Schema introspection, DDL extraction, Foreign key graph, Trigger parsing</span>]
<span style="color: cyan; font-weight: 600;">sqlite</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"SQLiteAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Schema introspection, DDL extraction, Virtual table detection</span>]

<span style="color: #888;"># --- Infrastructure Analyzers (Layer 1: uko-infra) ---</span>
<span style="color: cyan; font-weight: 600;">docker_compose</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"DockerComposeAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Service graph, Port mapping, Volume resolution, Environment variable extraction</span>]
<span style="color: cyan; font-weight: 600;">kubernetes</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"KubernetesAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Resource parsing, Service dependency graph, ConfigMap/Secret references, Ingress routing</span>]

Index Backend Providers
  backends:
    graph:
      - { name: "blazegraph", class: "BlazegraphBackend", features: ["SPARQL", "reasoning"] }
      - { name: "neo4j", class: "Neo4jBackend", features: ["Cypher", "APOC", "GDS"] }
    vector:
      - { name: "faiss", class: "FaissBackend", features: ["GPU acceleration", "HNSW"] }
      - { name: "qdrant", class: "QdrantBackend", features: ["filtering", "payloads"] }
  embedders:
    - { name: "openai", class: "OpenAIEmbedder", models: ["text-embedding-3-large"] }
    - { name: "local", class: "LocalEmbedder", models: ["all-MiniLM-L6-v2"] }
Adding a New UKO Vocabulary (e.g., for Terraform)
@prefix uko-tf: <https://cleveragents.ai/ontology/uko/infra/terraform#> .

uko-tf:TerraformModule a owl:Class ; rdfs:subClassOf uko-infra:ConfigBlock, uko:Container . uko-tf:Resource a owl:Class ; rdfs:subClassOf uko:Container . uko-tf:Variable a owl:Class ; rdfs:subClassOf uko-infra:ConfigKey . uko-tf:Output a owl:Class ; rdfs:subClassOf uko:Boundary . uko-tf:provisionsOn a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-tf:Resource ; rdfs:range uko-infra:Service .

Adding a New Context Strategy
class MyDomainStrategy:
    name = "my-namespace/domain-strategy"
    capabilities = StrategyCapabilities(
        uses_graph=True, uses_text=True,
        uko_levels=["uko:", "uko-tf:"],
        resource_types=["*"],
        supports_depth_breadth=True,
        quality_score=0.8,
    )
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">can_handle</span>(<span style="color: cyan;">self</span>, request, backends):
    <span style="color: magenta; font-weight: 600;">if</span> any(<span style="color: #66cc66;">"terraform"</span> <span style="color: magenta; font-weight: 600;">in</span> f <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> request.focus):
        <span style="color: magenta; font-weight: 600;">return</span> <span style="color: yellow;">0.9</span>
    <span style="color: magenta; font-weight: 600;">return</span> <span style="color: yellow;">0.0</span>

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">assemble</span>(<span style="color: cyan;">self</span>, request, backends, budget, plan_context):
    <span style="color: #888;"># Custom logic...</span>
    ...

Register via config:

[context.strategies.custom]
"my-namespace/domain-strategy" = "my_package.strategies.MyDomainStrategy"
Replacing Pipeline Components

Any of the 10 Context Assembly Pipeline components can be replaced by implementing the corresponding Protocol and registering the implementation. Components can be overridden at three scopes (plan > project > global), with more specific scopes taking precedence.

class MyCustomScorer:
    """A domain-aware fragment scorer that boosts infrastructure fragments
    when the plan focuses on deployment tasks."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">score</span>(<span style="color: cyan;">self</span>, fragments: <span style="color: cyan;">list</span>[ContextFragment],
          plan_context: PlanContext) -> <span style="color: cyan;">list</span>[ScoredFragment]:
    scored = []
    is_deploy = <span style="color: #66cc66;">"deploy"</span> <span style="color: magenta; font-weight: 600;">in</span> plan_context.plan.description.lower()
    <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> fragments:
        base_score = f.relevance_score * <span style="color: yellow;">0.4</span> + f.hierarchy_weight * <span style="color: yellow;">0.3</span>
        <span style="color: magenta; font-weight: 600;">if</span> is_deploy <span style="color: magenta; font-weight: 600;">and</span> <span style="color: #66cc66;">"uko-infra:"</span> <span style="color: magenta; font-weight: 600;">in</span> f.uko_uri:
            base_score *= <span style="color: yellow;">1.5</span>  <span style="color: #888;"># Boost infrastructure fragments</span>
        scored.append(ScoredFragment(**vars(f), composite_score=base_score))
    <span style="color: magenta; font-weight: 600;">return</span> sorted(scored, key=<span style="color: magenta; font-weight: 600;">lambda</span> s: s.composite_score, reverse=<span style="color: magenta; font-weight: 600;">True</span>)

Register via TOML config (global scope):

[context.pipeline]
fragment-scorer = "my_extensions.scorers:MyCustomScorer"

Or via project-level context view YAML:

project: local/api-service
view: strategize
pipeline:
  fragment-scorer: "my_extensions.scorers:MyCustomScorer"

The following table shows which Protocol each pipeline slot implements and what the default implementation provides:

Pipeline Slot Protocol Default Implementation Design Pattern
strategy-selector StrategySelectorProtocol ConfidenceWeightedSelector Strategy
budget-allocator BudgetAllocatorProtocol ProportionalBudgetAllocator Strategy
strategy-executor StrategyExecutorProtocol ParallelStrategyExecutor Circuit Breaker
fragment-deduplicator FragmentDeduplicatorProtocol ContentHashDeduplicator Strategy
detail-depth-resolver DetailDepthResolverProtocol MaxDepthResolver Specification
fragment-scorer FragmentScorerProtocol WeightedCompositeScorer Decorator
budget-packer BudgetPackerProtocol GreedyKnapsackPacker Iterator
fragment-orderer FragmentOrdererProtocol RelevanceCoherenceOrderer Template Method
preamble-generator PreambleGeneratorProtocol ProvenancePreambleGenerator Builder
skeleton-compressor SkeletonCompressorProtocol DepthReductionCompressor Visitor

Extension Points Summary

Extension Point Registration Mechanism Configuration Format Discovery
Custom Tools agents tool add --config <file> YAML Manual registration
MCP Server Tools Referenced in skill YAML mcp_servers section YAML Auto-discovered from MCP server
Agent Skills Standard Referenced in skill YAML agent_skills_dirs section Folder structure Auto-discovered from directory
Custom Skills agents skill add --config <file> YAML Manual registration
Custom Actors agents actor add --config <file> YAML Manual registration
Custom Resource Types agents resource type add --config <file> YAML Manual registration
Custom LLM Providers Python package installation + ProviderRegistry auto-discovery Python module Auto-discovered via langchain-* package naming
Custom Index Backends config.toml index.*.custom_module TOML + Python module Configuration-driven
Custom Sandbox Strategies Resource type sandbox_strategy field YAML + Python module Configuration-driven
Custom Automation Profiles agents automation-profile add --config <file> YAML Manual registration
Custom Invariant Actors Standard actor registration + assignment via --invariant-actor YAML Manual assignment
Custom UKO Analyzers config.toml context.uko.analyzers.custom.* TOML + Python module Configuration-driven
Custom UKO Vocabularies Turtle/RDF files + analyzer registration RDF/Turtle + TOML Configuration-driven
Custom Context Strategies config.toml context.strategies.custom.* TOML + Python module Configuration-driven
Pipeline: StrategySelector config.toml context.pipeline.strategy-selector or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain: plan > project > global > built-in)
Pipeline: BudgetAllocator config.toml context.pipeline.budget-allocator or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: StrategyExecutor config.toml context.pipeline.strategy-executor or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: FragmentDeduplicator config.toml context.pipeline.fragment-deduplicator or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: DetailDepthResolver config.toml context.pipeline.detail-depth-resolver or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: FragmentScorer config.toml context.pipeline.fragment-scorer or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: BudgetPacker config.toml context.pipeline.budget-packer or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: FragmentOrderer config.toml context.pipeline.fragment-orderer or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: PreambleGenerator config.toml context.pipeline.preamble-generator or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: SkeletonCompressor config.toml context.pipeline.skeleton-compressor or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)