ADR 0001 — The constellation cockpit becomes the dev-kit dashboard
- Status: Accepted (2026-06-25) — execution playbook in
docs/migration/cockpit-port.md. - Date: 2026-06-24 (accepted 2026-06-25)
- Context owner: Doug
- Scope:
@agentic-patterns/dashboard(rebuild) + a transport-agnostic graph engine; reuses@agentic-patterns/cli(ap playground+ discovery) and the@agentic-patterns/server/runtimeplumbing — with the small additive changes noted in the playbook (/admin/conversations,/agentscapability enrichment, provider-routing formalization).
Build/publish decision (locked 2026-06-25): develop in
packages/agent-dashboardlocally (bun run dev/ap playground); do NOT publish to npm until the chain graph + chat render against the real server. The dashboard isprivateand ships bundled inside@agentic-patterns/cli, so UI work needs no publish. Onlyruntime(provider routing) +server(conversations/capabilities) are publishable libs; ship them, thencli, at the milestone. Tune againstagents/*as the first suite is built. Full rationale + package table in the playbook.
First ADR in this repo — starts a
docs/adr/NNNN-*.mdseries alongside the existing topic docs indocs/.
Context
The dev-kit already has the hard parts of “launch + live-test my agents”:
- Discovery (
agent-cli/helpers/discover.ts): globsagents/**/agent.{ts,js,mjs}(oragents/<name>.agent.ts), dynamic-imports each, and normalizes the default export into anAgentRegistration{ id, name, description?, agent }(object or factory). The CLI injects therunner(env-driven viacreateRunner). ap playground: wires the observability stack (AgentEventBus→InMemoryEventCollector→InMemoryAdminService+SSEExporter+ optionalSQLiteExporter/EventStore), attaches the runner to each discovered agent, builds the HonocreateServer, mounts the dashboard SPA at/, opens the browser.@agentic-patterns/server(Hono):/agents,/conversations(run + stream),/admin/*(stats +/admin/events/streamSSE),/events,/health,/hooks.@agentic-patterns/dashboard(Vite + React 19, atomic structure): pages Agents / Chat / Conversations / Live / Tokens / Tools / ClaudeCode. It consumes the server’s SSE + admin/conversations endpoints. It has no agent-graph view, and its current UX isn’t where we want it.
Separately, a richer cockpit was built in a consumer project (retrieval-agent):
an agent-graph “constellation” with two projections (execution chain +
static composition — see that repo’s ADR 0005), a trace-from-events fold over
the same AgentEvent union the runtime emits, a polished design-system shell, a
system-aware light/dark theme, and a streaming chat/run-viewer. It is a strictly
richer dashboard than what ships today — it’s just wired to a bespoke Bun server.
Decision
- Rebuild
@agentic-patterns/dashboardaround the constellation cockpit. The cockpit’s graph + chat/run-viewer + design-system shell become the dashboard’s core; absorb the existing dashboard’s coverage (agents, conversations history, tokens/tools analytics, live) as views inside the new shell rather than discarding them (“steal from the original playground”). - Keep the framework plumbing as-is. Discovery,
ap playground, the Hono server, and the runtime (bus/collector/exporters/runner/EventStore) do not change. The dashboard is a pure consumer of the server’sAgentEventSSE +/agents//conversations//adminendpoints. - The two graph projections map onto the framework’s two native sources:
- chain (“what happened / will happen”) ← the
AgentEventSSE (a conversation stream, or/admin/events/stream). The fold already eats this union. - composition (“what this agent can do”) ← the discovered
AgentRegistration.agent’s declared capabilities/tools. This is the live-agent-development surface, sourced straight from discovery.
- chain (“what happened / will happen”) ← the
- The graph ENGINE is transport-agnostic and moves into the framework. The IR
(
Constellation), the per-frame fold (computeFrame), the replay hook, and theAgentEvent → TraceStepfold know nothing about transport. They land in the dashboard (or a shared@agentic-patterns/reactUI package) behind a thin adapter that maps the framework’s SSE/agent shapes to the engine’s inputs. - Uniform model-provider routing (carried-in requirement). Treat every
provider identically — no blocking
required(), no key-presence ladder. Define a hard list of supported providers —anthropic,openai,gemini,bifrost,ollama(for now) — map a model id to its provider (by prefix), and enable a provider iff its config is present; otherwise the model call fails at runtime with a clear error, never blocking config load. Rationale: the retrieval-agent cockpit today routes via a fixed key-presence ladder (src/runtime/config.tsbuildModel) that mis-routes once multiple keys coexist (e.g. a Gemini key set would capture anopenai/*orbifrost/*model). Open detail: where the localclaude-code/*Max-sub CLI path lives — ananthropicvariant, or its own provider entry.
Contract & boundaries
- Engine inputs: an ordered
AgentEvent[](live or replayed) + an optional agent composition (from the registration). Outputs: the rendered graph + replay/HUD. No transport knowledge. - Adapter (framework-specific): SSE (
/admin/events/stream, conversation streams) +/agentsregistrations → engine inputs. This is the only framework- coupled glue. - Framework-level UI: the constellation graph, the
ChatPanelorganism, theChatWorkspaceconsole shell, the theming/atoms, the event fold.- The
ChatWorkspace(cockpitcomponents/workspace/) is the full-screen three-pane agentic console — sessions (conversation library) · thread (theChatPanel) · a right rail of Context (the agent’s editable frame: persona, strategy, model, scope), Universe (the toolbox grouped by capability with blast radius + the data surface it can read), and Activity (the live constellation + an exploration trail — “watch it work”). It is the “regular agentic chat interface” surface and the natural shape for the rebuilt dashboard’s chat view. In the cockpit its Universe is sourced from a static catalog; promoted, Universe should read the discoveredAgentRegistration.agent’s declared composition (the “catalog coupling” open question below), and Sessions from/conversations. - The
ChatPanel(cockpitcomponents/chat/) is a reusable, streaming-first chat organism (atoms → molecules →Partrenderers) with auseChatmulti-turn driver and anapplyPartsreducer that folds theAgentEventunion (message.chunk/tool.start|end|rejected/thinking/error/message.complete) into aPart[](text|thinking|tool_call|error) — tolerant of both live camelCase and persisted rows. It was itself ported FROM this repo’sagent-dashboardchat lineage (its testeduseChatreducer + cc-viewer part renderers + atomic decomposition), so promoting it is upstreaming a refined version of the dashboard’s own chat, now with streaming tool-calls/thinking inline and asize="full"|"compact"API. It carrieschat/model.test.ts(10 reducer-contract tests). - Two folds, one event union: the constellation’s
trace-from-events(graph) and the chat’sapplyParts(conversation) are sibling reducers over the sameAgentEventstream. The framework gets one event-fold story driving both the graph and the chat from its SSE.
- The
- NOT framework (stays consumer-side or an optional dashboard plugin): domain evaluators like the retrieval-agent’s Lens/Eval slot-discipline grader.
Consequences
- One dashboard, graph-first, driven by discovery + the event bus:
ap playground→ chat with and watch any discovered agent as a live constellation; the composition view doubles as “what can this agent do” for agent authoring. - The retrieval-agent cockpit becomes a consumer: its three arms become
agents/*.agent.tsregistrations, it drives them throughap playgroundinstead of its bespoke Buncockpit.ts, and it keeps only its domain Lens/Eval as a plugin. (This is the consumer-side proof the discovery path drives real agents.) - Transport + persistence consolidate on the framework (Hono server +
better-sqlite3EventStore), retiring the cockpit’sbun:sqliteevals.db for framework use.
Open questions / risks
Verified during planning (2026-06-25, see playbook §5–6): the server streams named SSE (
event: <name>\ndata: <snake_case payload>) withmessage.delta(notmessage.chunk);GET /admin/conversationsis referenced by the dashboard but not implemented (must be added to back the session list);@agentic-patterns/runtimealready has aHybridModelResolverdoing prefix-based routing, so provider routing is formalize-and-extend, not build-from-scratch; and the dashboard already has ahooks/useChat.tsancestor that the ChatPanel port replaces.
- Per-run scope. Stateful/scoped agents (e.g. retrieval’s deal/as-of/scope) are
dependency-injected per conversation, but
AgentRegistration.agentis static. Needs a request-time context channel through/conversations— the one real integration question for scoped agents. Generic agents are unaffected. - Theming. Decide one token system for the dashboard: adopt the cockpit’s swe-brain CSS-var layer, or keep the dashboard’s own atoms and re-skin the constellation onto them. (The engine is var-driven, so either works.)
- Catalog coupling. The cockpit’s hard-coded
TOOL_CAPABILITYcatalog must be replaced by the agent’s declared composition from the registration. - Packaging. Engine in
@agentic-patterns/dashboardvs a new@agentic-patterns/reactconsumable beyond the dashboard. Lean: start in the dashboard; extract when a second consumer appears.
Non-goals
- Not changing discovery / server / runtime contracts.
- Not porting domain evaluators (Lens/Eval) into the framework.
- Not building the per-run-scope channel here (flagged as the follow-up).
Migration sketch (phased)
- Land the engine + SSE adapter in the dashboard → the chain graph appears for any agent’s conversation.
- Add the composition view from
/agentsregistrations. - Bring the
ChatWorkspaceconsole shell (sessions · thread · context/universe/ activity) as the chat view; restyle/absorb the existing pages (Agents / Conversations / Tokens / Tools / Live) under it. - Consumer-side: retrieval-agent arms →
agents/*.agent.ts; drive viaap playground; Lens/Eval as a plugin.