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
|
|
@ -883,22 +883,19 @@ fetch entirely.
|
|||
independent badge rather than being collapsed into one label,
|
||||
matching the existing multi-badge convention this line already uses
|
||||
for `paused`/`needs_update`/model/ctx.
|
||||
The row visual splits queued vs running: a **queued** entry (no
|
||||
transient yet, see below) shows only the pending-state pill (no row
|
||||
tint, so a long queue doesn't paint half the tab amber); a
|
||||
**running** entry (at least one transient) keeps the amber row tint
|
||||
AND draws a **rotating amber ring** around the agent icon, so it's
|
||||
obvious at a glance which container is actually moving.
|
||||
Any pending badge means the row is actually **running** something
|
||||
right now — there is no separate queued-but-not-started row state
|
||||
to visually distinguish it from (see **Pending-badge derivation**
|
||||
below), so every row carrying ≥1 badge keeps the amber row tint AND
|
||||
draws a **rotating amber ring** around the agent icon.
|
||||
|
||||
**Pending-badge derivation:** two separate stores, but no longer a
|
||||
priority *order* between them — the second only ever applies when
|
||||
the first has nothing to say. (1) **Transients**
|
||||
(`transientsState`, keyed `agent -> Map<kind, since_unix>`) — a
|
||||
transient is **derived from a job-queue node currently `Running`**
|
||||
against that agent, not declared per request, so its label follows
|
||||
the operation as it progresses (a rebuild reads `stop_for_update`,
|
||||
then `swap`, then `reconcile` rather than one constant `rebuilding`
|
||||
for its whole life). Two consequences for anything rendering it:
|
||||
**Pending-badge derivation:** transients only (`transientsState`,
|
||||
keyed `agent -> Map<kind, since_unix>`) — a transient is **derived
|
||||
from a job-queue node currently `Running`** against that agent, not
|
||||
declared per request, so its label follows the operation as it
|
||||
progresses (a rebuild reads `stop_for_update`, then `swap`, then
|
||||
`reconcile` rather than one constant `rebuilding` for its whole
|
||||
life). Two consequences for anything rendering it:
|
||||
|
||||
- The label vocabulary is **open** — it is the node's own wire tag
|
||||
(`NodeKind::as_str`, the same strings `NodeView.kind` carries),
|
||||
|
|
@ -916,24 +913,14 @@ fetch entirely.
|
|||
their own label directly via `TransientSet`/`TransientCleared`
|
||||
events carrying no backing node at all.
|
||||
|
||||
(2) The **job-queue fallback** (`jobqNodesState`, the flat
|
||||
`GraphNode[]` from `GET /api/jobq/graph` — `swarm.js`'s
|
||||
`refreshJobqGraph`, same generic endpoint the BU1LDS page's
|
||||
`<hive-jobq-graph>` self-fetches, see above) only fires when an
|
||||
agent has **zero** transients — since (1) now covers every
|
||||
`Running` node unconditionally, a `Running` job-queue node can
|
||||
never usefully reach this fallback by the time it's consulted; the
|
||||
fallback exists purely for the **`Pending` (queued, not yet
|
||||
started)** case, which `running_transients()`'s Running-only test
|
||||
cannot represent. `queuedOpsByAgent()` (swarm.js) reflects this: it
|
||||
only ever looks at `Pending`-state nodes carrying a non-empty
|
||||
`payload.data.agent`, and (mara, on review) the badge shows the
|
||||
node's raw `payload.label` string as-is — no English-phrase lookup
|
||||
translating it first, same opaque-string treatment a transient's
|
||||
own label already gets.
|
||||
`opRunning` (driving the `pending-running` row class + spinner) is
|
||||
simply "does this agent have at least one transient" — a queued-only
|
||||
entry (no transient yet) leaves it false.
|
||||
**Queued (not-yet-started) work shows nothing on the card.** An
|
||||
earlier version of this page had a job-queue-backed fallback badge
|
||||
for the `Pending` case (`queuedOpsByAgent()` in swarm.js, reading
|
||||
`jobqNodesState`); removed per mara, on review of hyperhive#3028's
|
||||
PR: *"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."* `jobqNodesState` still exists in swarm.js, but now
|
||||
feeds only the queue-summary banner below, not per-card badges.
|
||||
An **active model badge** (`model · <name>`, blue) appears when the
|
||||
container is running and the harness has persisted a model name
|
||||
(`harness/hyperhive-model`). Read by hive-c0re's `ContainerView`
|
||||
|
|
|
|||
|
|
@ -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