fix(#2465): render multi-agent DAG nodes as per-agent subgraph lines
renderQueueEntry flattened entry.nodes into one arrow-joined chain regardless of which agent each node belongs to. A multi-agent DAG (e.g. hivectl restart --graceful with several agents) runs independent per-agent subgraphs concurrently, no cross-agent deps, so joining them all into one sequential-looking chain misrepresented the actual DAG shape (mara's report: 'expected one dag that forks after the start node into the per agent sub dags'). Group nodes by n.agent (stable, first-seen order) and render one .rqe-nodes line per agent, with a small agent-label chip when the DAG spans more than one. Single-agent DAGs (the common case) collapse back to exactly the prior one-line render — no visible change there.
This commit is contained in:
parent
0d40583991
commit
45cdd62116
2 changed files with 47 additions and 18 deletions
|
|
@ -307,27 +307,52 @@ function renderQueueEntry(entry, _byId, isChild) {
|
|||
// state glyph, live step label, and build-log link. This is the
|
||||
// node-aware render that makes queue jumps / partial progress
|
||||
// visible (e.g. reconcile running while swap failed).
|
||||
//
|
||||
// A DAG can span multiple agents (e.g. a hive-wide restart) as
|
||||
// independent per-agent subgraphs with no cross-agent deps — they run
|
||||
// concurrently, not sequentially. Joining every node in array order
|
||||
// with a single `→` chain misrepresents that as one long sequential
|
||||
// pipeline. Group by `n.agent` (stable, first-seen order) and render
|
||||
// each agent's subgraph on its own line instead; single-agent DAGs
|
||||
// (the common case) collapse back to exactly the old one-line render.
|
||||
const nodes = entry.nodes || [];
|
||||
if (nodes.length) {
|
||||
const chain = el('div', { class: 'rqe-nodes' });
|
||||
nodes.forEach((n, i) => {
|
||||
if (i > 0) chain.append(el('span', { class: 'rqe-node-arrow' }, ' → '));
|
||||
const chip = el('span', {
|
||||
class: 'rqe-node rqe-node-' + n.state,
|
||||
title: n.kind + ' · ' + n.state + (n.error ? ' — ' + n.error : ''),
|
||||
},
|
||||
(QUEUE_STATE_GLYPH[n.state] || '?') + ' ' + (NODE_KIND_LABEL[n.kind] || n.kind));
|
||||
chain.append(chip);
|
||||
if (n.build_log_id != null) {
|
||||
chain.append(el('a', {
|
||||
class: 'rqe-log-link rqe-node-log',
|
||||
href: '/builds.html?id=' + n.build_log_id + '#buildlogs',
|
||||
target: '_blank',
|
||||
title: 'view build log #' + n.build_log_id,
|
||||
}, '⎙'));
|
||||
const groups = [];
|
||||
const groupByAgent = new Map();
|
||||
for (const n of nodes) {
|
||||
let g = groupByAgent.get(n.agent);
|
||||
if (!g) {
|
||||
g = [];
|
||||
groupByAgent.set(n.agent, g);
|
||||
groups.push([n.agent, g]);
|
||||
}
|
||||
});
|
||||
li.append(chain);
|
||||
g.push(n);
|
||||
}
|
||||
const multiAgent = groups.length > 1;
|
||||
for (const [agent, groupNodes] of groups) {
|
||||
const chain = el('div', { class: 'rqe-nodes' });
|
||||
if (multiAgent) {
|
||||
chain.append(el('code', { class: 'rqe-node-agent-label' }, agent));
|
||||
}
|
||||
groupNodes.forEach((n, i) => {
|
||||
if (i > 0) chain.append(el('span', { class: 'rqe-node-arrow' }, ' → '));
|
||||
const chip = el('span', {
|
||||
class: 'rqe-node rqe-node-' + n.state,
|
||||
title: n.kind + ' · ' + n.state + (n.error ? ' — ' + n.error : ''),
|
||||
},
|
||||
(QUEUE_STATE_GLYPH[n.state] || '?') + ' ' + (NODE_KIND_LABEL[n.kind] || n.kind));
|
||||
chain.append(chip);
|
||||
if (n.build_log_id != null) {
|
||||
chain.append(el('a', {
|
||||
class: 'rqe-log-link rqe-node-log',
|
||||
href: '/builds.html?id=' + n.build_log_id + '#buildlogs',
|
||||
target: '_blank',
|
||||
title: 'view build log #' + n.build_log_id,
|
||||
}, '⎙'));
|
||||
}
|
||||
});
|
||||
li.append(chain);
|
||||
}
|
||||
}
|
||||
const running = runningNode(entry);
|
||||
if (running && running.step) {
|
||||
|
|
|
|||
|
|
@ -161,6 +161,10 @@
|
|||
.rqe-node-cancelled { opacity: 0.55; text-decoration: line-through; }
|
||||
.rqe-node-arrow { color: var(--muted); }
|
||||
.rqe-node-log { margin-left: 0.1em; text-decoration: none; }
|
||||
/* Per-agent subgraph label, shown only when a DAG's nodes span more than
|
||||
one agent (independent concurrent subgraphs, one `.rqe-nodes` line each —
|
||||
see renderQueueEntry in builds.js). */
|
||||
.rqe-node-agent-label { color: var(--amber); margin-right: 0.3em; }
|
||||
.rqe-step {
|
||||
flex-basis: 100%;
|
||||
margin: 0.1em 0 0 1.8em;
|
||||
|
|
|
|||
Loading…
Reference in a new issue