Skip to content

Architecture

Audience: Engineers orienting to the monorepo for design or review work. Outcome: After reading, you know which package owns which concern, how they communicate, and where to dive deeper. Last verified: 2026-07-05

This document is the umbrella. It does not re-derive system internals — it points at the existing per-package architecture docs and the ADRs that ratify each decision. If you are new to the repo, read this in full first; if you are in a specific area, jump to the per-package link.


1. System at a glance

graph TB
    subgraph Clients["Clients"]
        CLI["agentic CLI
Typer"] UI["React 19 Dashboard
Vite 8 · @xyflow/react"] EXT["External callers
REST · WS · SSE"] end subgraph Runtime["agentic-workflows-v2/"] API["FastAPI Server
REST · WebSocket · SSE · 500-event replay"] ADR["AdapterRegistry
singleton"] NATIVE["Native DAG Engine
Kahn's algorithm · asyncio.wait FIRST_COMPLETED"] LG["LangGraph Engine
StateGraph · checkpointing"] ROUTER["SmartModelRouter
tier routing · circuit breakers · stats persistence"] RAG["RAG Pipeline
chunk · embed · index · retrieve · assemble"] AGENTS["Agents
Base + Coder + Reviewer + Architect + Orchestrator"] end subgraph Eval["agentic-v2-eval/"] RUNNERS["Batch · Streaming · AsyncStreaming Runners"] SCORER["Scorer
rubric-based"] REPORTER["Reporters
json · markdown · html"] end subgraph Shared["tools/ (agentic-tools)"] LLM["LLMClient
eight providers · nine routing backends"] BENCH["Benchmarks"] CACHE["Response cache"] end CLI --> ADR UI -->|REST · WS| API EXT -->|REST · WS · SSE| API API --> ADR ADR --> NATIVE ADR --> LG NATIVE --> AGENTS LG --> AGENTS AGENTS --> ROUTER AGENTS --> RAG ROUTER -->|provider calls| LLM API -.->|run JSON artifacts| RUNNERS RUNNERS --> SCORER SCORER --> REPORTER classDef rt fill:#4a90d9,stroke:#2c5f8a,color:#fff classDef ev fill:#00b894,stroke:#008060,color:#fff classDef sh fill:#fdcb6e,stroke:#c8a034,color:#333 class API,ADR,NATIVE,LG,ROUTER,RAG,AGENTS rt class SCORER,RUNNERS,REPORTER ev class LLM,BENCH,CACHE sh

The three Python packages have no source-tree imports between themtools/ is consumed as an installed workspace package (agentic-tools), like any other library, by both the runtime and the eval package. Beyond that:

  • The runtime and the offline eval harness integrate at the data level: the runtime persists run results as JSON artifacts, and agentic-v2-eval reads those files as input. Neither package imports the other.
  • The runtime's own in-server scoring (agentic_v2/scoring/, exposed via GET /api/runs/{filename}/evaluation) is independent of agentic-v2-eval — see integration-architecture.md.

2. Per-package deep dives

Package Entry point Deep dive
Runtime agentic-workflows-v2/agentic_v2/ architecture-runtime.md
UI agentic-workflows-v2/ui/src/ architecture-ui.md
Evaluation agentic-v2-eval/src/agentic_v2_eval/ architecture-eval.md
Shared tools tools/ architecture-tools.md
Cross-package integration integration-architecture.md

Additional supporting documents:


3. The five load-bearing mechanisms

These are the places where a change ripples across the system. Understand these before proposing architectural work.

3.1 Adapter registry

AdapterRegistry is a process-wide singleton in agentic_v2/adapters/registry.py. Engines register with a name (native, langchain), the CLI resolves --adapter <name> at runtime, and tests reset the singleton via an autouse fixture to prevent cross-test leakage. At FastAPI lifespan startup, AdapterRegistry.validate_selected() is called for the adapter named by AGENTIC_DEFAULT_ADAPTER; missing extras raise ConfigurationError with an install hint at boot time rather than mid-workflow.

  • Why it exists: ADR-001 — dual execution engine.
  • Current default: langchain (configurable per run); startup validation ratified by ADR-020.

3.2 Typed execution-event wire format

contracts/events.py defines a Pydantic discriminated union covering workflow_start, step_start, step_end, token_delta, step_complete, step_error, workflow_end, error, evaluation_start, evaluation_complete, approval_required, and approval_decision. WebSocket and SSE broadcasts validate before emit. The TypeScript union in ui/src/api/events.generated.ts is generated from this contract, and the wire-format-drift CI job fails any PR where the committed schemas or generated types diverge from the Python source of truth.

The same drift gate also covers four HTTP response shapes: DAGResponse, WorkflowInputSchemaResponse, WorkflowEditorStep, and RunsSummaryResponse. Their JSON schemas are committed under tests/schemas/ and regenerated via scripts/generate_schemas.py.

  • Ratifies: ADR-014.
  • Related: the 500-event replay buffer in server/websocket.py — clients reconnecting mid-run receive missed events.

3.3 SLO gates in git

Time-to-first-span p95 and nightly flake rate are stored as rolling windows in git — measurements are appended to JSON artifacts committed on each CI run, and the gate reads the window, not a fresh sample. This keeps the signal stable across single bad runs.

3.4 SmartModelRouter

Maps a numeric capability tier (ModelTier.TIER_0 through TIER_5 in models/router.py) → best available model at runtime. Health-weighted selection, exponential cooldowns, circuit breakers, persisted stats across restarts, Retry-After header awareness. Circuit-breaker state can be shared across workers via Redis (agentic_v2/models/redis_state.py) with an in-process fallback.

  • Ratifies: ADR-002.
  • Provider default for CI: GitHub Models via GITHUB_TOKEN — see ADR-016.

3.5 RAG pipeline

Fifteen modules in agentic_v2/rag/: loader → recursive chunker → embedder (content-hash dedup) → cosine vectorstore + BM25 keyword index → RRF hybrid retriever → token-budget assembler. Full OTEL tracing. Memory backed by MemoryStoreProtocol (InMemoryStore or RAGMemoryStore).

3.6 Core protocols

The seams above are held together by a small set of typing.Protocol contracts in agentic_v2/core/protocols.py. Implementations are structurally typed against these, so an engine, tool, or middleware can be swapped without touching call sites:

Protocol Contract
ExecutionEngine Runs a compiled workflow graph; the seam the adapter registry resolves (native, langchain).
SupportsStreaming Optional capability — an engine that emits incremental execution events.
SupportsCheckpointing Optional capability — an engine that persists and resumes run state.
AgentProtocol A single agent step: takes context, returns a typed result.
ToolProtocol An invocable tool exposed to agents through the MCP client stack.
DetectorProtocol A sanitization/threat detector applied to inbound content.
MiddlewareProtocol An ASGI-layer request/response interceptor (auth, rate limit, sanitization).
VerifierProtocol A post-step check that gates whether a result is accepted.

A CI drift check (scripts/check-doc-drift.py) fails any PR where a protocol defined in source is missing from this section.


4. The decision record

Every architecturally significant decision is captured as an ADR. The record currently spans 38 decisions across 36 files — from the founding dual-engine and router decisions (ADR-001/002) through wire-format contracts (ADR-014), authentication and tenancy (ADR-021/022), the ExecutionKit seam (ADR-023), scoring extraction (ADR-032), the RAG pipeline (ADR-035), and model discovery (ADR-036–040). Numbers 004–006 and 013 are intentionally unused gaps and must not be reclaimed.

The full, maintained index — with status, one-line summaries, and a suggested reading order — is adr/ADR-INDEX.md. This page deliberately does not duplicate that table.

Deeper mechanism docs that used to live here have moved to the runtime deep dive: core protocols, operability infrastructure (Redis, metrics, replay store, structured logging), the ExecutionKit ↔ runtime LLM seam (ADR-023), and the governance module are all covered in architecture-runtime.md.


5. What this document is not