swarm.js: drop per-agent pending-badge fallback, transients-only now
mara, on review: "swarm.js should not need to pull in the jobq to do its job" followed by "remove the per agent pending stuff - only show what is running." Deletes queuedOpsByAgent() entirely — no more per-agent badge derived from Pending-state job-queue nodes. A card's pending badges are now driven exclusively by transientsState (i.e. actually-running work); queued-but-not-started work shows nothing on the card until a node starts. jobqNodesState + refreshJobqGraph() stay, now feeding only the queue-summary banner (a separate, still-open question — mara separately asked for a dedicated rollup endpoint for that, tracked apart from this PR). Collapses the now-always-coincident `pending`/`pending-running` row classes into one (`pending-running`) — there's no more queued-only row state to visually distinguish it from. docs/web-ui/dashboard.md's Container-row section rewritten to match: the two-store priority-fallback description is gone, replaced with "transients only."
This commit is contained in:
parent
45710ab739
commit
17f61d1da6
4 changed files with 66 additions and 106 deletions
|
|
@ -347,8 +347,11 @@ hive-agent-menu {
|
|||
.container-row:hover hive-agent-menu {
|
||||
--menu-btn-opacity: 1;
|
||||
}
|
||||
/* Pending state splits queued vs running. */
|
||||
.container-row.pending .actions { opacity: 0.4; pointer-events: none; }
|
||||
/* Card actions dim while a row has any pending-state badge (transient-
|
||||
driven only now — see swarm.js's pending-badge derivation comment;
|
||||
there is no separate queued-but-not-running row state to tell apart
|
||||
from this one anymore). */
|
||||
.container-row.pending-running .actions { opacity: 0.4; pointer-events: none; }
|
||||
.container-row.pending-running {
|
||||
border-color: var(--amber);
|
||||
background: color-mix(in srgb, var(--amber) 5%, transparent);
|
||||
|
|
|
|||
|
|
@ -28,16 +28,18 @@ const CTX_CAUTION_TOKENS = 100_000; // fallback yellow threshold (~= 50% of 200k
|
|||
// ─── module-level state ─────────────────────────────────────────────────────
|
||||
|
||||
// Flat GraphNode[] from GET /api/jobq/graph (hive-jobq-wire's generic
|
||||
// shape — see hive-jobq-wire/src/lib.rs) — the last DagView/NodeView
|
||||
// (job_queue-specific) read left on this page, migrated off in favour of
|
||||
// the generic endpoint builds.js already established the pattern for
|
||||
// (see mountJobqGraph there). "Nothing is hidden" on this endpoint: every
|
||||
// retained job group's nodes ride the wire, `Done` included, and each
|
||||
// group's root is an ordinary node (`parent: null`) whose own `state` IS
|
||||
// the group's roll-up already — unlike the old DagView, no client-side
|
||||
// rollup derivation is needed, just an explicit filter for what's still
|
||||
// active wherever that's wanted (see queuedOpsByAgent + the queue-summary
|
||||
// banner in renderContainers).
|
||||
// shape — see hive-jobq-wire/src/lib.rs) — feeds *only* the queue-summary
|
||||
// banner in renderContainers (mara, on review: "swarm.js should not need
|
||||
// to pull in the jobq to do its job" — the per-agent pending-row fallback
|
||||
// that used to be this state's other consumer is gone, see
|
||||
// queuedOpsByAgent's removal below). Once a dedicated rollup endpoint
|
||||
// exists (status-count pairs, requested separately) the banner moves
|
||||
// onto that instead and this fetch goes away entirely. "Nothing is
|
||||
// hidden" on this endpoint: every retained job group's nodes ride the
|
||||
// wire, `Done` included, and each group's root is an ordinary node
|
||||
// (`parent: null`) whose own `state` IS the group's roll-up already —
|
||||
// unlike the old DagView, no client-side rollup derivation is needed,
|
||||
// just an explicit filter for what's still active.
|
||||
let jobqNodesState = [];
|
||||
|
||||
// Keyed container row cache. Maps agent name -> { el: <li>, fingerprint }.
|
||||
|
|
@ -90,44 +92,13 @@ export function applyRebuildQueueChanged() {
|
|||
// longer reads it. Purely a "something changed, go refetch" signal.
|
||||
refreshJobqGraph();
|
||||
}
|
||||
// Map from agent name -> the queued node's raw `payload.label` string,
|
||||
// backing the SW4RM card's fallback pending badge. Pending only — every
|
||||
// *running* node naming an agent already lights a transient pill (any
|
||||
// running node, not just a curated "worth it" subset — see
|
||||
// docs/web-ui.md::Container row), so a Running entry here would
|
||||
// always be redundant with `transientsState` by the time this is
|
||||
// consulted. Queued (not yet started) work is the one state
|
||||
// transients can't represent, since `running_transients()` on the
|
||||
// backend is a Running-only test.
|
||||
//
|
||||
// The returned label is displayed as-is (mara, on review: "drop
|
||||
// queuedLabelFor - just show what the backend sends") — same opaque-
|
||||
// string treatment `pending`'s transient half already gets, no
|
||||
// English-phrase lookup table translating it first.
|
||||
//
|
||||
// Agent is per-node, not per-group (a group can span agents — e.g. the
|
||||
// startup sweep's MetaLock cascade, or a hive-wide restart), so this
|
||||
// derives each agent's queued state from whichever of its own nodes is
|
||||
// pending, not from the group root. No "skip agentless/spawn nodes"
|
||||
// filter is needed here (an earlier version of this function keyed on
|
||||
// a DAG-level `kind` field that no longer exists on the wire — dead
|
||||
// code since that refactor): a node with no `data.agent` is silently
|
||||
// skipped below, and an agent with no existing container row never
|
||||
// gets its map entry read regardless (see containersState in state.js
|
||||
// — it's populated straight from the real container roster, not a
|
||||
// pre-spawn placeholder).
|
||||
function queuedOpsByAgent() {
|
||||
const out = new Map();
|
||||
for (const n of jobqNodesState) {
|
||||
if (n.state !== 'Pending') continue;
|
||||
const agent = n.payload.data && n.payload.data.agent;
|
||||
if (!agent) continue;
|
||||
// First node found wins — with only one state to consider
|
||||
// (Pending), there's no priority to resolve between groups.
|
||||
if (!out.has(agent)) out.set(agent, n.payload.label);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
// Per-agent queued-but-not-running badges used to have their own
|
||||
// fallback here (`queuedOpsByAgent()`, reading `Pending`-state nodes
|
||||
// off `jobqNodesState`). Removed per mara, on review: "remove the per
|
||||
// agent pending stuff - only show what is running" — a card's pending
|
||||
// badges are now driven by `transientsState` alone (see below), which
|
||||
// is exactly "what is running": queued-not-yet-started work shows
|
||||
// nothing on the card until a node actually starts.
|
||||
|
||||
// ─── transients ─────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -366,9 +337,14 @@ function buildContainerLi(c, node, opts) {
|
|||
askerCount, targetCount, agentQCount,
|
||||
url, containerBase, forgeBase, s,
|
||||
} = opts;
|
||||
// A single `pending-running` class now covers the whole "has at
|
||||
// least one badge" state — there's no more queued-but-not-running
|
||||
// row to distinguish it from (see the pending-badge derivation
|
||||
// comment in renderContainers), so the separate no-tint `pending`
|
||||
// class from before that removal is gone rather than kept as a
|
||||
// class that would now always co-occur with this one.
|
||||
const li = el('li', {
|
||||
class: 'container-row'
|
||||
+ (pending.length ? ' pending' : '')
|
||||
+ (opRunning ? ' pending-running' : '')
|
||||
+ (selected ? ' selected' : ''),
|
||||
});
|
||||
|
|
@ -704,10 +680,6 @@ export function renderContainers(s) {
|
|||
const forgeBase = (s && s.forge_public_url) || null;
|
||||
const ul = existingUl ?? el('ul', { class: 'containers' });
|
||||
const tree = buildAgentTree(containers);
|
||||
// Queued (not-yet-running) ops per agent name — see
|
||||
// docs/web-ui.md::Container row for why this only covers the
|
||||
// Pending case now (Running is fully covered by transientsState).
|
||||
const queuedOps = queuedOpsByAgent();
|
||||
|
||||
// Build the ordered list of <li> elements, reusing cached rows
|
||||
// whose displayed state hasn't changed.
|
||||
|
|
@ -721,20 +693,17 @@ export function renderContainers(s) {
|
|||
const containerBase = gatewayLinks
|
||||
? `/agent/${encodeURIComponent(c.name)}`
|
||||
: `http://${hostname}:${c.port}`;
|
||||
// Pending-badge derivation: an agent's transients win outright when
|
||||
// any exist (rendered one badge per pill — mara: "show all running
|
||||
// nodes that name the agent"), the rebuild-queue's queued-only
|
||||
// fallback otherwise. See docs/web-ui.md::Container row.
|
||||
// Pending-badge derivation: transients only — "what is running,"
|
||||
// full stop (mara, on review: "remove the per agent pending stuff -
|
||||
// only show what is running"; rendered one badge per pill — mara,
|
||||
// earlier: "show all running nodes that name the agent"). See
|
||||
// docs/web-ui.md::Container row.
|
||||
const transientKindsMap = transientsState.get(c.name);
|
||||
// Sorted for stable badge order across renders — Map iteration
|
||||
// order is insertion order, which shifts as pills clear/re-add.
|
||||
const transientKinds = transientKindsMap
|
||||
const pending = transientKindsMap
|
||||
? Array.from(transientKindsMap.keys()).sort() : [];
|
||||
const queuedKind = transientKinds.length === 0 ? queuedOps.get(c.name) : null;
|
||||
const pending = transientKinds.length > 0
|
||||
? transientKinds
|
||||
: (queuedKind ? [queuedKind] : []);
|
||||
const opRunning = transientKinds.length > 0;
|
||||
const opRunning = pending.length > 0;
|
||||
const selected = selectionState.has(c.name);
|
||||
// Pending questions where this agent is the asker (awaiting an
|
||||
// answer) or the target (owes a reply). Derived live from
|
||||
|
|
|
|||
|
|
@ -234,13 +234,14 @@ window.marked = marked;
|
|||
// `transientsState` + `containersState`, not from `s.*`).
|
||||
syncTransientsFromSnapshot(s);
|
||||
syncContainersFromSnapshot(s);
|
||||
// Job-queue graph feeds the SW4RM agent-card badges
|
||||
// (queuedOpsByAgent) + the queue-summary banner; its own detailed
|
||||
// view lives on /builds.html. Self-fetches GET /api/jobq/graph —
|
||||
// not read off `s` (this page's snapshot carries no jobq field) —
|
||||
// fire-and-forget: renderContainers below runs off whatever
|
||||
// jobqNodesState already holds, and refreshJobqGraph's own
|
||||
// re-render catches up once the fetch resolves.
|
||||
// Job-queue graph feeds only the SW4RM queue-summary banner now
|
||||
// (per-agent card badges are transient-only — see swarm.js); its
|
||||
// own detailed view lives on /builds.html. Self-fetches GET
|
||||
// /api/jobq/graph — not read off `s` (this page's snapshot
|
||||
// carries no jobq field) — fire-and-forget: renderContainers
|
||||
// below runs off whatever jobqNodesState already holds, and
|
||||
// refreshJobqGraph's own re-render catches up once the fetch
|
||||
// resolves.
|
||||
refreshJobqGraph();
|
||||
renderContainers(s);
|
||||
// Sync the derived approvals + questions stores from the
|
||||
|
|
|
|||
Loading…
Reference in a new issue