swarm.js: restore the queue-summary banner on GET /api/jobq/rollup
hyperhive#3033 (jobq rollup endpoint) merged, unblocking hyperhive#3036. The banner (removed on PR#3031 rather than ship it on an interim GET /api/jobq/graph client-side derivation) is back, now reading GET /api/jobq/rollup — hive-jobq-wire::state_rollup's pre-tallied Vec<StateCount>, not the full graph. running sums the Running and Finishing entries' roots (Finishing = own work done, subtree still going); queued reads the Pending entry's roots. roots specifically, matching the banner's established "N whole operations" meaning, not the endpoint's parallel nodes count (~7 nodes per rebuild, 1 root). Re-adds the rebuild_queue_changed SSE subscription dropped alongside the banner, wired as a payload-less refetch trigger — confirmed with atlas on the DagView-deletion tracker that this is the intended final shape (keep the event, drop the payload) rather than deleting it and falling back to polling. Verified the running/queued derivation against constructed StateCount[] fixtures (running+queued mix, Finishing-counts-as-running, settled states never contribute, multi-state sums) before touching swarm.js — 6/6 checks passed. npm run build clean, tracker-tag + comment-block pre-push lints clean. docs/web-ui/dashboard.md updated to describe the restored banner + the two prior shapes it went through.
This commit is contained in:
parent
4cf647aa70
commit
4ec1c61d52
4 changed files with 124 additions and 69 deletions
|
|
@ -209,12 +209,12 @@ Three sub-tabs: **R3BU1LD QU3U3** (default), **M3T4 1NPUTS**,
|
||||||
**BUILD L0GS**. Its own esbuild bundle (`builds.js`); cold-loads
|
**BUILD L0GS**. Its own esbuild bundle (`builds.js`); cold-loads
|
||||||
`/api/state` and subscribes to `/api/dashboard/stream` for
|
`/api/state` and subscribes to `/api/dashboard/stream` for
|
||||||
`rebuild_queue_changed`, `meta_inputs_changed`, `meta_update_running`.
|
`rebuild_queue_changed`, `meta_inputs_changed`, `meta_update_running`.
|
||||||
The SW4RM tab (`/dashboard.html`) does **not** read this endpoint (or
|
The SW4RM tab (`/dashboard.html`) does **not** read this endpoint at
|
||||||
any job-queue state) at all — mara, on review: "swarm.js should not
|
all — mara, on review: "swarm.js should not need to pull in the jobq
|
||||||
need to pull in the jobq to do its job." Its per-agent pending badges
|
to do its job." Its per-agent pending badges are transient-only, and
|
||||||
are transient-only, and its queue-summary banner is removed for now
|
its queue-summary banner reads the much narrower `GET /api/jobq/rollup`
|
||||||
(see Container row, below), pending a dedicated rollup endpoint it
|
instead (see Container row, below) — a handful of pre-tallied counts,
|
||||||
can read directly instead of the full graph.
|
not the graph.
|
||||||
|
|
||||||
**R3BU1LD QU3U3** — pending, in-flight, and recently-settled container
|
**R3BU1LD QU3U3** — pending, in-flight, and recently-settled container
|
||||||
operations: rebuilds, meta-update cascades, and first-spawns. One
|
operations: rebuilds, meta-update cascades, and first-spawns. One
|
||||||
|
|
@ -915,16 +915,15 @@ fetch entirely.
|
||||||
their own label directly via `TransientSet`/`TransientCleared`
|
their own label directly via `TransientSet`/`TransientCleared`
|
||||||
events carrying no backing node at all.
|
events carrying no backing node at all.
|
||||||
|
|
||||||
**Queued (not-yet-started) work shows nothing on the card, and
|
**Queued (not-yet-started) work shows nothing on the card.** An
|
||||||
`swarm.js` reads no job-queue state at all.** An earlier version of
|
earlier version of this page had a job-queue-backed fallback badge
|
||||||
this page had a job-queue-backed fallback badge for the `Pending`
|
for the `Pending` case (`queuedOpsByAgent()`, reading a `GET
|
||||||
case (`queuedOpsByAgent()`, reading a `GET /api/jobq/graph` fetch);
|
/api/jobq/graph` fetch); removed per mara, on review of
|
||||||
removed per mara, on review of hyperhive#3028's PR: *"swarm.js
|
hyperhive#3028's PR: *"swarm.js should not need to pull in the jobq
|
||||||
should not need to pull in the jobq to do its job,"* followed by
|
to do its job,"* followed by *"remove the per agent pending stuff -
|
||||||
*"remove the per agent pending stuff - only show what is running."*
|
only show what is running."* Per-agent badges never came back — only
|
||||||
The queue-summary banner below was, for the same reason, also pulled
|
the queue-summary banner below did, once a narrow enough endpoint
|
||||||
rather than kept on that same client-side graph-fetch — see its own
|
existed for it to read instead of the full graph.
|
||||||
entry below for why.
|
|
||||||
An **active model badge** (`model · <name>`, blue) appears when the
|
An **active model badge** (`model · <name>`, blue) appears when the
|
||||||
container is running and the harness has persisted a model name
|
container is running and the harness has persisted a model name
|
||||||
(`harness/hyperhive-model`). Read by hive-c0re's `ContainerView`
|
(`harness/hyperhive-model`). Read by hive-c0re's `ContainerView`
|
||||||
|
|
@ -965,23 +964,28 @@ per-agent actions and navigation links. Contents:
|
||||||
agent is stale. Banner pulses on each broker SSE event
|
agent is stale. Banner pulses on each broker SSE event
|
||||||
(`pulseBanner` with a 4s grace timer).
|
(`pulseBanner` with a 4s grace timer).
|
||||||
|
|
||||||
**Build-queue summary banner — currently removed, coming back on a
|
**Build-queue summary banner** — when the job queue has any active
|
||||||
different data source.** Previously a compact amber banner above the
|
work, a compact amber banner sits above the container list: `◐ build
|
||||||
container list (`◐ build queue — N running · M queued — view
|
queue — N running · M queued — view queue →` (the link goes to the
|
||||||
queue →`, linking to the BU1LDS page), derived client-side from `GET
|
BU1LDS page's R3BU1LD QU3U3). Reads `GET /api/jobq/rollup`
|
||||||
/api/jobq/graph` (filter to `parent: null` group roots — each an
|
(`hive-jobq-wire::state_rollup`, `jobqRollupState` in `swarm.js`) —
|
||||||
independent operation, not a raw node count — bucket by `state`).
|
`Vec<{ state, nodes, roots }>`, every lifecycle state present in a
|
||||||
Pulled per mara, on review of hyperhive#3028's PR: *"dont replace one
|
fixed order, zero counts included — rather than the full
|
||||||
legacy thing with another. then we will have to either wait with
|
`/api/jobq/graph` tree: `running` sums the `Running` and `Finishing`
|
||||||
this pr or split it into what can and cannot be done now"* — a
|
entries' `roots` (`Finishing` = own work done, subtree still going,
|
||||||
client-side derivation over the generic graph was itself judged a
|
still in flight), `queued` reads the `Pending` entry's `roots`.
|
||||||
stopgap not worth landing, the same way the old `DagView` read it
|
`roots` specifically, not `nodes` — the banner has always meant *N
|
||||||
replaced was. Comes back once a dedicated rollup endpoint (status ×
|
whole operations*, not raw steps (one rebuild is ~7 nodes but 1
|
||||||
count pairs, tracked separately) exists for `swarm.js` to read
|
root); `nodes` exists on the same endpoint for a consumer that wants
|
||||||
directly instead of pulling the whole graph in for a summary. Until
|
step-level counts instead, unused here.
|
||||||
then, the actual running work per agent is still visible on each
|
|
||||||
card via the transient pills; only the hive-wide at-a-glance count is
|
This banner went through two prior shapes before landing here, both
|
||||||
missing.
|
per mara review comments on hyperhive#3028's PR: a client-side
|
||||||
|
derivation over the full graph (*"swarm.js should not need to pull in
|
||||||
|
the jobq to do its job"*), then removed entirely rather than keep
|
||||||
|
that interim fetch (*"dont replace one legacy thing with another"*).
|
||||||
|
Restored once the dedicated rollup endpoint (hyperhive#3033) existed
|
||||||
|
for it to read directly instead.
|
||||||
|
|
||||||
### Themed dialogs
|
### Themed dialogs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -458,11 +458,7 @@ hive-agent-menu {
|
||||||
|
|
||||||
/* Build-queue summary banner on the SW4RM tab: one compact line
|
/* Build-queue summary banner on the SW4RM tab: one compact line
|
||||||
when the rebuild queue has active work, with a link to the full queue on
|
when the rebuild queue has active work, with a link to the full queue on
|
||||||
the C0R3 page. Amber to match the in-progress / "rebuilding" card tint.
|
the C0R3 page. Amber to match the in-progress / "rebuilding" card tint. */
|
||||||
Currently unused — swarm.js dropped the banner pending a dedicated
|
|
||||||
rollup endpoint (see swarm.js's transients-section comment) — kept
|
|
||||||
here rather than deleted-then-restored, since the markup/classes are
|
|
||||||
expected to come back unchanged once that endpoint exists. */
|
|
||||||
.queue-summary {
|
.queue-summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// SW4RM (containers) domain — extracted from tabs.js.
|
// SW4RM (containers) domain — extracted from tabs.js.
|
||||||
// Agent topology, container-row rendering, selection bar, peer-hives block,
|
// Agent topology, container-row rendering, selection bar, peer-hives block,
|
||||||
// and all live-update apply handlers for container-state and transient ops.
|
// and all live-update apply handlers for container-state, transient, and
|
||||||
// See docs/web-ui.md::Container row for the rendering contract.
|
// job-queue-rollup ops. See docs/web-ui.md::Container row for the
|
||||||
|
// rendering contract.
|
||||||
|
|
||||||
import {
|
import {
|
||||||
$, form, fmtAgeSecs,
|
$, form, fmtAgeSecs,
|
||||||
|
|
@ -50,20 +51,49 @@ const transientsState = new Map();
|
||||||
// tab-gated visibility).
|
// tab-gated visibility).
|
||||||
const selectionState = new Set();
|
const selectionState = new Set();
|
||||||
|
|
||||||
// ─── transients ─────────────────────────────────────────────────────────────
|
// ─── job queue rollup (queue-summary banner only) ──────────────────────────
|
||||||
//
|
//
|
||||||
// This is now the ONLY job-queue-derived state on this page — mara, on
|
// The ONE piece of job-queue-derived state on this page, and deliberately
|
||||||
// review of the DagView migration: "swarm.js should not need to pull in
|
// narrow — mara, on review of the DagView migration: "swarm.js should not
|
||||||
// the jobq to do its job." An earlier version of this file also fetched
|
// need to pull in the jobq to do its job." An earlier version of this file
|
||||||
// GET /api/jobq/graph directly for two things now gone: a per-agent
|
// fetched GET /api/jobq/graph (the full node tree) for two things: a
|
||||||
// queued-but-not-running badge (`queuedOpsByAgent()`, removed per "remove
|
// per-agent queued-but-not-running badge (`queuedOpsByAgent()`, removed
|
||||||
// the per agent pending stuff - only show what is running" — a card's
|
// per "remove the per agent pending stuff - only show what is running" —
|
||||||
// pending badges are transients-only now, which already means "what is
|
// a card's pending badges are transients-only now, which already means
|
||||||
// running") and the queue-summary banner (removed per "dont replace one
|
// "what is running") and this banner, which was pulled entirely per "dont
|
||||||
// legacy thing with another" — a client-side derivation over the generic
|
// replace one legacy thing with another" (a client-side tally over the
|
||||||
// graph was itself judged a stopgap not worth shipping; the banner comes
|
// generic graph was itself judged a stopgap). Now that the dedicated
|
||||||
// back once the dedicated rollup endpoint exists, and reads that
|
// rollup endpoint exists (hive-jobq-wire::state_rollup, served at
|
||||||
// directly instead of pulling the graph in here at all).
|
// GET /api/jobq/rollup), the banner reads *that* instead — a handful of
|
||||||
|
// pre-tallied counts, not the graph.
|
||||||
|
let jobqRollupState = [];
|
||||||
|
|
||||||
|
// Fetches the rollup fresh and re-renders. Called on cold load (see
|
||||||
|
// tabs.js's refreshState) and whenever `rebuild_queue_changed` fires
|
||||||
|
// (applyRebuildQueueChanged below) — a payload-less push trigger by
|
||||||
|
// design, confirmed with atlas on the jobq-deletion tracker: the event
|
||||||
|
// carries no `queue` field this page reads, same "something changed,
|
||||||
|
// go refetch" treatment builds.js already gives it for
|
||||||
|
// <hive-jobq-graph>.refresh().
|
||||||
|
// Best-effort: a failed fetch leaves the previous snapshot in place
|
||||||
|
// rather than wiping the banner on a network blip.
|
||||||
|
export async function refreshJobqRollup() {
|
||||||
|
let counts;
|
||||||
|
try {
|
||||||
|
const r = await fetch('/api/jobq/rollup');
|
||||||
|
if (!r.ok) return;
|
||||||
|
counts = await r.json();
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jobqRollupState = counts;
|
||||||
|
renderContainersFromState();
|
||||||
|
}
|
||||||
|
export function applyRebuildQueueChanged() {
|
||||||
|
refreshJobqRollup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── transients ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function syncTransientsFromSnapshot(s) {
|
export function syncTransientsFromSnapshot(s) {
|
||||||
transientsState.clear();
|
transientsState.clear();
|
||||||
|
|
@ -583,15 +613,31 @@ export function renderContainers(s) {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// No queue-summary banner for now — the previous version derived it
|
// Queue-summary banner: one compact line above the container list when
|
||||||
// client-side from GET /api/jobq/graph, which mara flagged as
|
// the job queue has active work, linking to the full queue on the
|
||||||
// replacing one legacy DagView-shaped hack with another rather than
|
// BU1LDS page. Reads GET /api/jobq/rollup's pre-tallied `roots` counts
|
||||||
// landing the real fix ("dont replace one legacy thing with another
|
// (see jobqRollupState above) rather than the full graph — `roots`
|
||||||
// ... split it into what can and cannot be done now"). Comes back once
|
// because "N running / M queued" has always meant *operations*, not
|
||||||
// the dedicated rollup endpoint exists, reading that directly. Until
|
// raw steps (one rebuild is ~7 nodes but 1 root); `nodes` exists on
|
||||||
// then the queue's actual state is still visible per-agent on each
|
// the same endpoint for a consumer that wants steps instead, unused
|
||||||
// card via the transient pills; only the hive-wide at-a-glance summary
|
// here. `Finishing` counts as running (own work done, subtree still
|
||||||
// is missing.
|
// going, still in flight) — same treatment `roots` gets nowhere else,
|
||||||
|
// since the rollup endpoint doesn't collapse the two itself.
|
||||||
|
const byState = new Map(jobqRollupState.map((c) => [c.state, c]));
|
||||||
|
const running = (byState.get('Running')?.roots ?? 0)
|
||||||
|
+ (byState.get('Finishing')?.roots ?? 0);
|
||||||
|
const queued = byState.get('Pending')?.roots ?? 0;
|
||||||
|
if (running || queued) {
|
||||||
|
const parts = [];
|
||||||
|
if (running) parts.push(`${running} running`);
|
||||||
|
if (queued) parts.push(`${queued} queued`);
|
||||||
|
root.append(el('div', { class: 'queue-summary' },
|
||||||
|
el('span', { class: 'glyph spinner' }, '◐'), ' ',
|
||||||
|
el('strong', {}, 'build queue'), ' — ',
|
||||||
|
parts.join(' · '), ' ',
|
||||||
|
el('a', { class: 'queue-summary-link', href: '/builds.html' }, 'view queue →'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if (!containers.length && !transientsState.size) {
|
if (!containers.length && !transientsState.size) {
|
||||||
root.append(el('p', { class: 'empty' }, 'no managed containers'));
|
root.append(el('p', { class: 'empty' }, 'no managed containers'));
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ import {
|
||||||
renderQuestions, activeQuestionCount,
|
renderQuestions, activeQuestionCount,
|
||||||
} from './call.js';
|
} from './call.js';
|
||||||
import {
|
import {
|
||||||
syncTransientsFromSnapshot,
|
refreshJobqRollup, syncTransientsFromSnapshot,
|
||||||
applyContainerStateChanged, applyContainerRemoved,
|
applyRebuildQueueChanged, applyContainerStateChanged, applyContainerRemoved,
|
||||||
applyTransientSet, applyTransientCleared,
|
applyTransientSet, applyTransientCleared,
|
||||||
renderContainers, renderContainersFromState,
|
renderContainers, renderContainersFromState,
|
||||||
renderSelectionBar, renderPeerHives,
|
renderSelectionBar, renderPeerHives,
|
||||||
|
|
@ -234,6 +234,14 @@ window.marked = marked;
|
||||||
// `transientsState` + `containersState`, not from `s.*`).
|
// `transientsState` + `containersState`, not from `s.*`).
|
||||||
syncTransientsFromSnapshot(s);
|
syncTransientsFromSnapshot(s);
|
||||||
syncContainersFromSnapshot(s);
|
syncContainersFromSnapshot(s);
|
||||||
|
// Job-queue rollup feeds only the SW4RM queue-summary banner
|
||||||
|
// (per-agent card badges are transient-only — see swarm.js).
|
||||||
|
// Self-fetches GET /api/jobq/rollup — not read off `s` (this
|
||||||
|
// page's snapshot carries no jobq field) — fire-and-forget:
|
||||||
|
// renderContainers below runs off whatever jobqRollupState
|
||||||
|
// already holds, and refreshJobqRollup's own re-render catches
|
||||||
|
// up once the fetch resolves.
|
||||||
|
refreshJobqRollup();
|
||||||
renderContainers(s);
|
renderContainers(s);
|
||||||
// Sync the derived approvals + questions stores from the
|
// Sync the derived approvals + questions stores from the
|
||||||
// snapshot, then render. Live `*_added` / `*_resolved` events
|
// snapshot, then render. Live `*_added` / `*_resolved` events
|
||||||
|
|
@ -304,10 +312,11 @@ window.marked = marked;
|
||||||
container_removed: applyContainerRemoved,
|
container_removed: applyContainerRemoved,
|
||||||
// tombstones_changed / meta_inputs_changed / meta_update_running are
|
// tombstones_changed / meta_inputs_changed / meta_update_running are
|
||||||
// handled on /core.html now (the SYST3M panels moved there).
|
// handled on /core.html now (the SYST3M panels moved there).
|
||||||
// rebuild_queue_changed: this page no longer subscribes to it at
|
// rebuild_queue_changed: refreshes the SW4RM queue-summary banner
|
||||||
// all (see swarm.js's transients-section comment) — the SW4RM tab
|
// (see swarm.js) — a payload-less push trigger, same treatment
|
||||||
// has nothing left that reads it, unlike /builds.html which still
|
// /builds.html gives it for <hive-jobq-graph>.refresh() (its own
|
||||||
// does (its own separate subscription).
|
// separate subscription).
|
||||||
|
rebuild_queue_changed: applyRebuildQueueChanged,
|
||||||
schedules_changed: applySchedulesChanged,
|
schedules_changed: applySchedulesChanged,
|
||||||
capabilities_changed: applyCapabilitiesChanged,
|
capabilities_changed: applyCapabilitiesChanged,
|
||||||
tool_groups_changed: applyToolGroupsChanged,
|
tool_groups_changed: applyToolGroupsChanged,
|
||||||
|
|
@ -329,7 +338,7 @@ window.marked = marked;
|
||||||
const es = openStream(
|
const es = openStream(
|
||||||
'/api/dashboard/stream?kinds=sent,approval_added,approval_resolved,' +
|
'/api/dashboard/stream?kinds=sent,approval_added,approval_resolved,' +
|
||||||
'question_added,question_resolved,transient_set,transient_cleared,' +
|
'question_added,question_resolved,transient_set,transient_cleared,' +
|
||||||
'container_state_changed,container_removed,' +
|
'container_state_changed,container_removed,rebuild_queue_changed,' +
|
||||||
'schedules_changed,capabilities_changed,tool_groups_changed',
|
'schedules_changed,capabilities_changed,tool_groups_changed',
|
||||||
);
|
);
|
||||||
es.onmessage = (e) => {
|
es.onmessage = (e) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue