diff --git a/frontend/packages/dashboard/src/swarm.js b/frontend/packages/dashboard/src/swarm.js index 6f48ee6b..bbc17e67 100644 --- a/frontend/packages/dashboard/src/swarm.js +++ b/frontend/packages/dashboard/src/swarm.js @@ -59,11 +59,18 @@ export function applyRebuildQueueChanged(ev) { // See docs/web-ui.md::Container row for the badge taxonomy. renderContainersFromState(); } -// Map from agent name -> highest-priority in-flight queue entry -// (`running` beats `queued`). Used by the container row renderer -// to surface "building..." / "meta-updating..." badges on the -// SW4RM tab when an op is still in the rebuild queue but no -// operator-initiated transient is set. +// Map from agent name -> highest-priority in-flight op ({ kind, state }) +// (`running` beats `queued`). Used by the container row renderer to +// surface "building..." / "meta-updating..." badges on the SW4RM tab +// when an op is still in the rebuild queue but no operator-initiated +// transient is set. +// +// Agent is per-node, not per-DAG (a DAG can span agents — e.g. the +// startup sweep's MetaLock cascade, or a hive-wide restart), so this +// derives each agent's in-flight state from its own node(s) within the +// entry rather than the DAG's overall `state`/`kind` — a DAG can be +// `running` overall while a given agent's subgraph hasn't started yet +// (still queued behind an earlier node in its chain), and vice versa. function inFlightOpsByAgent() { const out = new Map(); for (const e of rebuildQueueState) { @@ -74,10 +81,20 @@ function inFlightOpsByAgent() { // surfacing if the spawn op happens to land in the queue // while the row exists transiently. if (e.kind === 'spawn') continue; - const cur = out.get(e.agent); - // Prefer running over queued; otherwise keep the first match. - if (!cur || (cur.state === 'queued' && e.state === 'running')) { - out.set(e.agent, e); + const perAgentState = new Map(); + for (const n of e.nodes || []) { + if (!n.agent) continue; + if (n.state !== 'queued' && n.state !== 'running') continue; + const cur = perAgentState.get(n.agent); + if (!cur || (cur === 'queued' && n.state === 'running')) { + perAgentState.set(n.agent, n.state); + } + } + for (const [agent, state] of perAgentState) { + const cur = out.get(agent); + if (!cur || (cur.state === 'queued' && state === 'running')) { + out.set(agent, { kind: e.kind, state }); + } } } return out;