A map of the codebase: the layer model, the subsystem dependency graph, the inbound message flow, and a per-namespace-family table (~112 namespaces under
src/+src-clients/). The exhaustive per-file table was retired in favour of the family table below — it stays accurate across refactors.
Dvergr is a Clojure AI-agent harness organized as an 8-layer stack (L0–L7) over a
copy-on-write substrate and a reactive (FRP) runtime. The architecture has
converged on Rooms + Participants on a Bus as the single discourse model, with
medium adapters (dvergr.adapters.core) bridging external chat media (Telegram)
into rooms as remote-user actors. A room is a self-contained project (its own git
workspace + Datahike message/knowledge stores); a thin global system-db
(dvergr.system.db) is only the registry that keeps the set of rooms/agents together.
Layer summary:
substrate/config), git
worktree CoW (substrate/git), state-root paths (substrate/paths), logging
(substrate/log), the katzen-bound knowledge schema (kb/schema), exact-text +
semantic code parsing (code), and per-domain Datahike schema (scheduler/schema).runtime/bus + runtime/peer-bus, the single reactive runtime/clock, the
CoW execution-context memory model (runtime/ctx), and the pure cron math
(scheduler/cron).chat.context (signals + Datahike + SCI),
chat.schema (the unified Datahike schema), chat.accounting (microdollar budgets),
participant.context, the per-[room,agent] folded ChatContext (agent/room-context),
Room store impls (room/store/*), the system.db registry and the in-memory code
index.discourse core (Room/Participant/Message, fork/merge,
algebra: ask/fan-out/race/quorum/pipeline), the room.registry, rooms (CRUD +
Datahike unification), rooms-as-projects provisioning (system.rooms, system.mail),
reactive room views (rooms/messages, rooms/tree, rooms/stats, rooms/forks), the
per-room reactive scheduler (rooms/scheduler), and the transport-agnostic adapters/core.tools registry/executor, the SCI sandbox (sandbox +
sandbox/ns/* injectors + sandbox/workspace load-root + gated sandbox/deps),
structural/analyzer code-edit utilities, the channels framework, and git worktree
management. The read-only intake data sources are no longer in dvergr's source —
they live as agent-editable Clojure in the
dvergr-sandbox stdlib repo, cloned into
each room's workspace; only intake/bash (the muschel shell) and intake/mail remain
in-tree.chat/agent), context compaction, model
abstraction (model/* + provider impls for Anthropic/OpenAI, native Codex
subscription, and CLI compatibility paths), the
LLM-backed participant factory (discourse/llm), the shared turn mechanics
(agent/turn), the system-prompt assembler (agent/prompt), and process
checkpoint/resume (agent/process).daemon (still a large monolith — turn loop + session
handling + Telegram adapter + evaluator inlined), the scheduler, actors + transport,
skills + the unified definitions loader, tasks, stats, personas, the
central ops spec, and the Telegram channel.core facade, MCP server/json-rpc, the nREPL client, the CLI
entry point, the TUI app, and the web dashboard/API.Key cross-cutting threads. Spindel's ExecutionContext is the spine: rooms,
schedules, the peer-bus, and stats all live as ctx-local state under [:dvergr/*]
paths. Canonical Spindel world forks over registered Yggdrasil systems underpin the
fork/review/merge lifecycle (rooms/forks + the spawn_agent/propose_change tools).
The largest remaining structural debt:
daemon monolith (inlined turn loop + session handling + Telegram adapter + evaluator);channels/telegram, still partly on the old dispatch path rather than adapters/core;:status :online presence assumptions in skills/stats that pre-date the
resolved "presence = room membership" model.graph TD
L7["L7 Clients: core / mcp / client / cli / tui / web"]
L6["L6 Orchestration: daemon / scheduler / actors / skills / definitions / tasks / personas / ops"]
L5["L5 Agent Execution: chat.agent / compaction / model.* / discourse.llm / agent.turn / agent.process"]
L4["L4 Tools and Sandbox: tools / sandbox.* / workspace / channels / git (intakes live in the sandbox repo)"]
L3["L3 Discourse + Rooms: discourse / rooms / registry / adapters / system.rooms"]
L2["L2 Per-room State: chat.context / chat.schema / accounting / room.store / system.db"]
L1["L1 Reactive Runtime: spindel FRP / bus / clock / ctx / cron"]
L0["L0 CoW Substrate: datahike / git / config / paths / code / kb.schema"]
L7 --> L6 --> L5 --> L4 --> L3 --> L2 --> L1 --> L0
graph TD
daemon[daemon]
core[core-facade]
discourse[discourse]
discLLM[discourse.llm]
adapters[adapters.core]
rooms[rooms]
sysrooms[system.rooms]
sysdb[system.db]
bus[bus]
clock[runtime.clock]
registry[room.registry]
roomstore[room.store]
chatCtx[chat.context]
chatAgent[chat.agent]
compaction[chat.compaction]
schema[chat.schema]
model[model.*]
tools[tools]
sandbox[sandbox]
actors[actors]
definitions[definitions]
skills[skills]
tasks[tasks]
scheduler[scheduler.core]
roomsched[rooms.scheduler]
process[agent.process]
turn[agent.turn]
roomctx[agent.room-context]
partctx[participant.context]
channels[channels.core]
telegram[channels.telegram]
core --> discourse
core --> discLLM
core --> bus
core --> partctx
daemon --> discourse
daemon --> adapters
daemon --> roomctx
daemon --> rooms
daemon --> sysrooms
daemon --> scheduler
daemon --> actors
daemon --> skills
daemon --> tools
daemon --> channels
daemon --> telegram
daemon --> clock
discLLM --> discourse
discLLM --> chatCtx
discLLM --> chatAgent
discLLM --> compaction
discLLM --> turn
discLLM --> process
discLLM --> tools
adapters --> discourse
rooms --> discourse
rooms --> roomstore
rooms --> registry
rooms --> schema
sysrooms --> sysdb
sysrooms --> rooms
roomsched --> clock
roomsched --> discourse
roomctx --> discourse
roomctx --> chatCtx
roomctx --> registry
roomctx --> bus
discourse --> bus
discourse --> registry
discourse --> roomstore
chatAgent --> chatCtx
chatAgent --> compaction
chatAgent --> model
chatAgent --> tools
compaction --> chatCtx
compaction --> model
chatCtx --> schema
chatCtx --> sandbox
tools --> sandbox
tools --> process
skills --> actors
definitions --> skills
scheduler --> actors
scheduler --> discourse
process --> chatCtx
channels --> tools
telegram --> channels
graph TD
ext["External medium (Telegram update)"]
chan["channels.telegram polling"]
adapter["adapters.core inbound!"]
actorUp["actors: upsert external-user actor"]
room["rooms / discourse Room post! as user"]
busN["bus publish (room-scoped)"]
roomctx["agent.room-context fold into ChatContext signal"]
part["discourse.llm Participant on-message"]
turn["chat.agent run-agent-turn!"]
modelc["model.chat -> provider (anthropic/openai/claude-code) SSE"]
toolexec["tools/execute (role-scoped, sandbox SCI)"]
reply["agent reply post! to Room"]
egress["adapters.core egress-participant send-fn"]
out["External medium outbound"]
ext --> chan --> adapter --> actorUp --> room --> busN --> roomctx --> part --> turn --> modelc
turn --> toolexec --> turn
turn --> reply --> busN
reply --> egress --> out
| family | layer | role |
|---|---|---|
substrate/{config,git,log,paths} | L0 | EDN config load/cache; git worktree CoW; structured logging; .dvergr/ state-root resolution |
code + code/index | L0/L2 | Clojure source parse/diff + in-memory katzen ACSet code index |
kb/schema | L0 | Knowledge-base schema (katzen canonical → Datahike idents) |
scheduler/{schema,cron,core,tools} | L0–L6 | persistent-schedule schema; pure cron math; spindel-native scheduler; agent schedule tools |
runtime/{bus,peer_bus,clock,ctx} | L1 | pub/sub substrate; control-plane peer-bus; the single reactive clock; CoW execution-context memory model |
chat/{context,schema,accounting,compaction,agent,tool_schema} | L2/L5 | per-chat signals+Datahike state; the unified schema; budgets; compaction; the core turn loop; tool→schema gen |
participant/context | L2 | uniform participant context (LLM/human/hybrid) |
agent/room_context | L2 | per-[room,agent] long-lived ChatContext folded from the room bus |
system/{db,rooms,mail} | L2/L3 | the global system-db registry (identity backbone); rooms-as-projects provisioning/resolvers; attach a briefkasten mailbox as a ygg system |
room/{registry,store,store/datahike,store/memory} | L2/L3 | slug↔Room registry; PRoomStore protocol + Datahike/in-memory impls |
discourse (core) | L3 | Room/Participant/Message, fork/merge, algebra combinators |
discourse/{background,enrichment,human,commands,personas,definitions,attention} | L3/L6 | background spawner; on-message decorators; human participant; slash-command registry; pre-built personas; the unified skill+agent-identity loader; pure attention decisions |
discourse/{llm,generation} | L4/L5 | LLM participant factory; GenerationHandle bridge |
rooms + rooms/{forks,messages,tree,stats,theme,scheduler} | L3 | room CRUD + Datahike unification; fork describe/review/merge/discard; signal-backed transcript/tree/stats views; per-speaker theme; per-room reactive scheduler |
adapters/core | L3 | transport-agnostic medium adapter: inbound posting + egress Participant |
tools + tools/{structural,llm_call,approval} | L4 | tool registry/executor (role-scoped); structural edits; one-shot LLM tool; approval workflow |
effects, authority, resource + resource/authority | L4 | the effect boundary between sandbox code and the world (doc/effects.md); the can? authorization seam; conserved budgets and spindel's resource authority over the ledger (doc/unified-worlds.md) |
drive/{core,fs,blobs,integration} | L4 | the built-in room drive mounted at /drive: file nodes, the muschel FS, content-addressed blobs, host wiring |
io/{frozen_web,acquisition}, search/secondary, media/{doc,vision}, audio/{stt,record} | L4 | frozen web for discovery experiments; HTTP acquisition records; fulltext over Datahike's scriptum secondary index; document text and vision; speech-to-text |
sandbox + sandbox/{deps,workspace} + sandbox/ns/* | L4 | SCI runtime (ctx/eval/limits); gated add-libs; the workspace load-root; the injected namespaces (io/data/datahike/agent/kb/room/mail/codec/dev/intake) |
intake/{bash,mail} | L4 | the only in-tree intakes — muschel-jailed shell + briefkasten mail (all other data sources live in the dvergr-sandbox stdlib repo) |
channels/{core,telegram,telegram_commands,telegram_send} | L4/L6 | channel framework; Telegram Bot API (polling/old dispatch); dvergr.ops slash-command binding; outbound Markdown→HTML rendering + chunking |
model/{provider,providers,registry,chat,quirks,gateway} + model/api/{anthropic,openai,claude_code,codex_subscription,codex_auth} | L5 | provider protocol + registry + metadata; origin-confined credential injection; HTTP/SSE, native Codex subscription, and CLI compatibility transports; provider quirks |
agent/{turn,prompt,process,tool_commands,persona,ops,fields} | L5/L6 | shared turn mechanics; system-prompt assembler; checkpoint/resume process; tool commands; persona resolution; agent-management ops + field spec |
agent/{program,run,world,roster,workflow} | L5/L6 | hire! and agent programs; durable Runs; a Run's forked work world; immutable rosters; one task run N times on forks |
agent/{evaluation,environment,experiment,experiment/*,verifiers,evaluators,attempt,spend,trajectory,conversation,episode} | L5/L6 | the evaluation path (doc/evaluation-model.md): Attempts, EnvironmentDefs, experiments and their runner, preflight and statistics, verifier trust tiers, spend, trajectory export, certified conversations |
catalog + catalog/{wiki,wiki_gen,room,room_run,casepack,report,workspace} | L6 | catalog workflows (the wiki family) and room workflows (doc/room-workflows.md): bundles, case packs, room-run, report.md |
jobs, artifact, activity | L6 | long operations as durable Runs; content-addressed portable values; durable observations on room messages |
orchestration/{daemon,skills,tasks,stats} | L6 | the runtime daemon (lifecycle/registry/turn loop/sessions/Telegram); skill registry; task ledger; stats cache |
actors + actors/transport | L6 | durable actor identity table; PActorTransport impls |
ops | L6 | central operations spec — one data map; web/MCP/Telegram surfaces derived (datahike-spec pattern) |
security/allowlist | L6 | Telegram user allowlist access control |
core | L7 | public API facade (re-exports discourse/llm/personas/bus) |
mcp/{server,json_rpc,http,surface,repl} | L7 | TCP/stdio MCP server + JSON-RPC/MCP dispatch; Streamable HTTP; profiles, toolsets and result encoding; the REPL's dvergr.ops (doc/mcp.md) |
clients/client | L7 | nREPL client — inspect/interact + fork-task workflow over a daemon |
cli/main + tui/app (src-clients) | L7 | CLI entry point; the TUI app (a rich medium adapter onto rooms) |
web/{server,dashboard,api,ops,agents} | L7 | http-kit server; dashboard (hiccup+HTMX); spec-derived JSON API; web ops binding; agent config UI |
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |