Dependency tiers
Modules placed by measured import fan-in — the real coupling, not the org chart. Each tier depends only on the tiers below it. Hover a tier to focus it.
Result[T] · NewType ids · enums · utc_now · Settings · provider registry
The one-way boundary, precisely
No Layer-2 package is a structural dependency of the base. Three import sites cross down, with differing mechanisms: orchestration → knowledge.protocols.KnowledgeGraph (runtime Protocol), security → audit.protocols.AuditLog (TYPE_CHECKING only), and security → audit.models (a lazy, in-method concrete import that dodges a parse-time cycle). No base module imports meta at all.
The node hot-path
Every node travels one path: ContextNodeExecutor.execute_node. Features aren't branches — they're stations on this line. An unwired interceptor pipeline is a no-op; behaviour is byte-identical to running without it.
Resolve
Prep context
PRE interceptors · run_pre
Recovery loop ≤ max_recovery_attempts (default 2)
Emit
The two dispatch seams
All extensibility lives here. Both are split single-method @runtime_checkable Protocols — a POST-only interceptor needn't implement pre.
Seam 1 · NodeResolver chain orchestration/resolvers/
First matches() wins → ResolveOutcome = RunAgent | NodeComplete. Add a node kind = register a resolver, not edit code.
config["council"] → deliberate, vote, return verdictSPEC-10config["capability"] only when an agent_selector is wired; else falls throughSPEC-09RunAgent(node.ref_id) · universal fallbackbaseSeam 2 · Interceptor spine interceptors/ SPEC-01a
Ordered PRE → execute → POST. Empty = no-op.
pre() → ACCEPT (enrich ctx) | REJECTPREpost() → ACCEPT | REJECT | RECOVERPOSTon_failure = REJECT (block) | RECOVER (retry + hint)gateAgentic behaviours
An Agent[GoalT, ResultT] is a typed protocol resolved from the registry. Single agents, competitive auctions, and deliberative councils are three ways the engine decides who answers — plus the self-hosting meta-agents that build the framework itself.
Built-in agentsagents/
Librarian · Researcher · Summarizer · Writer — each a typed Agent[Goal,Result], registered in the AgentRegistry.
Meta-agents (Layer 2)meta/agents.py
ArchitectAgent · AgentSynthesizer · AuditAgent · KnowledgeGraphAgent · DreamAgent · SolutionDesignerAgent — standard Agent[Goal,Result] impls that introspect & extend CEMAF.
Auction selection SPEC-09
N agents bid on a capability; deterministic max-bid wins. Capabilities: RESEARCH · SUMMARIZE · WRITE · LIBRARY · QUALITY.
score = 0.5·match + 0.3·(1−load) + 0.2·budget_headroom
Tie-break is deterministic; absent a selector, nodes resolve statically.
Agent council SPEC-10
N members deliberate on one question; a pluggable VoteAggregator decides. Methods: MAJORITY · WEIGHTED · QUORUM · UNANIMOUS.
Multi-round (rounds=N): round 2+ broadcasts prior opinions; members may revise; early-stops when the tally settles. The verdict becomes the node output and steers the DAG.
The capability substrate
Agents act through Tools (@tool, schema-validated) and Skills (multi-tool kits). The polyglot coding kit runs inside a ShellSandbox (cwd-confined, time/output-bounded, network-screened). A bounded IterationLoop (SPEC-08) parses pytest/ruff/mypy failures into FailureSignals and re-attempts until tests pass — feedback as a first-class control structure.
Context lifecycle
Context is the asset; tokens are the cost; provenance is the audit trail. The Context object is immutable (copy-on-write) — every mutation carries a typed ContextPatch source.
agent.run sees; a ProvenanceLink records it.Selection algorithms
Greedy · Knapsack · Optimal — drop from the bottom of the priority order when over budget. Exclusions are tracked with a reason (BUDGET_EXCEEDED, LOW_PRIORITY).
Memory scopes & tiers
Scopes: SESSION · PROJECT · USER · GLOBAL. Tiered store L0/L1/L2 progressive recall; semantic + episodic; dedup on write.
Context types
RESOURCE · MEMORY · SKILL — each with behaviour rules (cacheable, shareable, compressible, TTL, default priority).
Provenance
Every patch records source · reason · correlation_id; every node run records a ProvenanceLink. Silent mutation is forbidden.
Runtime & the reactive plane
One composition root wires ~23 injectable dependencies; the executor runs the DAG and emits events. Quality, audit, and learning subscribe asynchronously — off the hot path.
create_executor() base root
Builds the ContextNodeExecutor (threads all 23 RuntimeServices deps incl. max_recovery_attempts), assembles the resolver chain [Council, Auction?, Static], subscribes online-eval + quality-police when an event bus is present, wraps in an InstrumentedDAGExecutor when a tracer is present.
create_meta_executor() Layer-2 root
Auto-builds AuditTrail from the EventBus and a KnowledgeGraph from the MemoryManager, registers the meta-agents/tools, then delegates to create_executor — so Layer 2 inherits every base primitive (incl. the RECOVER budget) for free.
TASK_STARTED · TASK_COMPLETED · TASK_FAILED · DAG_STARTED · DAG_COMPLETED · DAG_CHECKPOINT · SYSTEM_ERROR
OnlineEvalPipeline
consumes task events, scores output, re-emits EVAL_COMPLETED. evals/online.py
QualityPolice
subscribes to EVAL_COMPLETED (not raw task events); z-score anomaly + halt gate. evals/police.py
AuditTrail
EventBus → AuditEntry; feeds the KnowledgeGraph. audit/subscriber.py
BlueprintHarvester
distils high-scoring runs into reusable blueprints. blueprint/harvest.py
Memory subscriber
persists memory events. events/memory_subscriber.py
Two-hop chain: the executor emits task events → OnlineEvalPipeline re-emits EVAL_COMPLETED → QualityPolice subscribes to that. recovery_attempts and gate_rejected ride the task payload so audit & harvest correlate recovery without re-walking results.
The architecture in five sentences
If you remember nothing else.
1 Protocol-first
Every integration point (LLMClient, VectorStore, MemoryManager, Evaluator, NodeResolver, Interceptor, KnowledgeGraph, AuditLog) is a @runtime_checkable Protocol; implementations are structural — bring your own.
2 One hot-path, many stations
Features are PRE/POST interceptor stations and resolver entries on a single execute_node flow — not bespoke branches.
3 Immutable data + provenance
Context is copy-on-write; every mutation carries a ContextPatch source; every node run records a ProvenanceLink.
4 Budgeted, priority-selected context
TokenBudget + ContextCompiler drop by priority, not recency, before each agent call.
5 Strict layering
Layer 2 self-hosting consumes the base only through contracts; the base never imports it — so CEMAF introspects itself without a cycle.