refactor(#2441): update queue consumers + docs for agent-per-node

DagView no longer has a DAG-level agent, so consumers derive it from the
per-node agents:
- hivectl dag_progress.rs: dag_agents(d) helper (distinct node agents,
  comma-joined) in place of d.agent.
- dashboard builds.js: entryAgents(entry) helper likewise for the
  rebuild-queue card + live-log header + cancel confirms.
- docs/coordinator.md: lease prose (node-agent-keyed, global per agent),
  wire shape (NodeView.agent, no DagView.agent), and dropped the removed
  dedup section.
This commit is contained in:
atlas 2026-07-14 21:42:37 +02:00 committed by mara
commit e2b48d2014
3 changed files with 66 additions and 36 deletions

View file

@ -165,11 +165,22 @@ function firstFailedNode(entry) {
return (entry.nodes || []).find((n) => n.state === 'failed') || null;
}
// Distinct agents across a DAG's nodes, comma-joined for display. Agent is
// per-node now (a DAG can span agents, e.g. a hive-wide restart), so there's
// no DAG-level `agent` field — derive it from the nodes.
function entryAgents(entry) {
const seen = [];
for (const n of entry.nodes || []) {
if (n.agent && !seen.includes(n.agent)) seen.push(n.agent);
}
return seen.join(',');
}
function rebuildQueueEntryFingerprint(entry, isChild) {
return JSON.stringify({
state: entry.state,
kind: entry.kind,
agent: entry.agent,
agent: entryAgents(entry),
source: entry.source,
started_at: entry.started_at,
enqueued_at: entry.enqueued_at,
@ -265,7 +276,7 @@ function renderQueueEntry(entry, _byId, isChild) {
el('span', { class: 'rqe-kind', title: entry.kind },
(QUEUE_KIND_GLYPH[entry.kind] || '?') + ' ' + entry.kind),
' ',
el('code', { class: 'rqe-agent' }, entry.agent),
el('code', { class: 'rqe-agent' }, entryAgents(entry)),
);
li.append(' ', el('span', { class: 'rqe-source rqe-source-' + entry.source }, entry.source));
if (entry.state === 'queued') {
@ -331,14 +342,14 @@ function renderQueueEntry(entry, _byId, isChild) {
class: 'inline rqe-cancel',
'data-async': '',
'data-confirm':
`cancel ${entry.kind} for \`${entry.agent}\` (queue id ${entry.id})? ` +
`cancel ${entry.kind} for \`${entryAgents(entry)}\` (queue id ${entry.id})? ` +
`the row drops from the queue and never runs. running / done / failed entries can't be cancelled this way.`,
});
cancelForm.append(el('button', {
type: 'submit',
class: 'rqe-cancel-btn',
title: 'cancel this queued ' + entry.kind,
'aria-label': 'cancel queued ' + entry.kind + ' for ' + entry.agent,
'aria-label': 'cancel queued ' + entry.kind + ' for ' + entryAgents(entry),
}, '✗'));
li.append(cancelForm);
}
@ -414,7 +425,7 @@ function renderRebuildLiveLog(queue) {
const header = el('div', { class: 'rebuild-live-log-header' },
toggle, ' ',
el('span', { class: 'rebuild-live-log-title' }, 'live build log — '),
el('code', { class: 'rqe-agent' }, running.agent),
el('code', { class: 'rqe-agent' }, entryAgents(running)),
' ', el('span', { class: 'rqe-kind' },
(running.kind || 'rebuild') + ' · ' + (NODE_KIND_LABEL[liveNode.kind] || liveNode.kind)),
' ', badge, ' ',