@Coordinator + @Fleet
The annotation model that produces the events.
modules/coordinator/README.md
Once you have a @Coordinator with three or four crew members, the
question “which agent is doing the heavy lifting?” stops being
answerable from logs. You need a graph. Atmosphere has one.
Every @Coordinator writes to CoordinationJournal. The journal is
the same event stream your durable HITL + audit surfaces read. The
admin plane exposes two REST endpoints that turn the journal into a
graph payload your UI can render.
ceo dispatched research twelve times (eleven successful,
averaging 842 ms) and finance six times (all successful,
averaging 304 ms). Exactly what the two endpoints below return in
JSON.
GET /api/admin/flowRender the entire journal as a flow graph. Optional
lookbackMinutes=N constrains the window; omit for “all recorded
history”.
curl http://localhost:8080/api/admin/flow?lookbackMinutes=60{ "nodes": [ {"id": "ceo", "label": "ceo"}, {"id": "research-agent", "label": "research-agent"}, {"id": "finance-agent", "label": "finance-agent"} ], "edges": [ { "from": "ceo", "to": "research-agent", "dispatches": 12, "successes": 11, "failures": 1, "averageDurationMs": 842 }, { "from": "ceo", "to": "finance-agent", "dispatches": 6, "successes": 6, "failures": 0, "averageDurationMs": 304 } ]}The shape is deliberately framework-agnostic — feed it to any graph library (vis.js, Cytoscape, D3 force-directed, React Flow) without post-processing.
GET /api/admin/flow/{coordinationId}Scope the graph to a single coordination run — the natural target for a drilldown when a user clicks a run in the audit log or admin dashboard.
Minimal React + React Flow skeleton:
import ReactFlow from 'reactflow';import { useEffect, useState } from 'react';
export function CoordinatorFlow() { const [data, setData] = useState({ nodes: [], edges: [] });
useEffect(() => { const load = () => fetch('/api/admin/flow?lookbackMinutes=60') .then(r => r.json()) .then(setData); load(); const t = setInterval(load, 5000); return () => clearInterval(t); }, []);
const nodes = data.nodes.map((n, i) => ({ id: n.id, data: { label: n.label }, position: { x: i * 180, y: 120 }, })); const edges = data.edges.map((e, i) => ({ id: `e-${i}`, source: e.from, target: e.to, label: `${e.successes}/${e.dispatches} ${e.averageDurationMs}ms`, style: { stroke: e.failures > 0 ? '#d33' : '#666' }, }));
return <ReactFlow nodes={nodes} edges={edges} fitView />;}Under 60 lines and you have a live coordinator dashboard.
dispatches — raw dispatch count from parent → child.successes — AgentCompleted events observed for the edge.failures — AgentFailed events. Edge turns red when non-zero.averageDurationMs — mean Duration across successful runs.
Sudden regressions here are the first signal of a degraded agent.If successes + failures < dispatches, the remaining calls are
in-flight — useful for surfacing stuck agents.
/api/admin/flow and /api/admin/flow/{coordinationId} are read
endpoints. Default posture leaves them anonymous so a local demo
console works without credentials.
Write endpoints (broadcast / disconnect / cancel) have their own
triple-gate (feature flag → Principal → ControlAuthorizer) —
see modules/admin/README.md
for the full wiring.
Three possibilities:
@Coordinator on the classpath. The flow endpoint returns
{"nodes": [], "edges": []} — not an error.atmosphere-coordinator is on the classpath; plain
servlet users install via ServiceLoader or by writing to
CoordinatorProcessor.COORDINATION_JOURNAL_PROPERTY.lookbackMinutes is too tight. Drop the parameter to see
everything the journal has retained.@Coordinator + @Fleet
The annotation model that produces the events.
modules/coordinator/README.md
Three-Phase Trust Adoption
PermissionMode + audit surfaces complement the flow viewer.
/tutorial/25-trust-phases/
Dynatrace 2026 Agentic AI report
Calls out 44% of respondents reviewing agent-to-agent flows manually because the data is unstructured. This endpoint is the fix.