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:
iris 2026-08-03 21:07:53 +02:00
commit 4ec1c61d52
4 changed files with 124 additions and 69 deletions

View file

@ -42,8 +42,8 @@ import {
renderQuestions, activeQuestionCount,
} from './call.js';
import {
syncTransientsFromSnapshot,
applyContainerStateChanged, applyContainerRemoved,
refreshJobqRollup, syncTransientsFromSnapshot,
applyRebuildQueueChanged, applyContainerStateChanged, applyContainerRemoved,
applyTransientSet, applyTransientCleared,
renderContainers, renderContainersFromState,
renderSelectionBar, renderPeerHives,
@ -234,6 +234,14 @@ window.marked = marked;
// `transientsState` + `containersState`, not from `s.*`).
syncTransientsFromSnapshot(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);
// Sync the derived approvals + questions stores from the
// snapshot, then render. Live `*_added` / `*_resolved` events
@ -304,10 +312,11 @@ window.marked = marked;
container_removed: applyContainerRemoved,
// tombstones_changed / meta_inputs_changed / meta_update_running are
// handled on /core.html now (the SYST3M panels moved there).
// rebuild_queue_changed: this page no longer subscribes to it at
// all (see swarm.js's transients-section comment) — the SW4RM tab
// has nothing left that reads it, unlike /builds.html which still
// does (its own separate subscription).
// rebuild_queue_changed: refreshes the SW4RM queue-summary banner
// (see swarm.js) — a payload-less push trigger, same treatment
// /builds.html gives it for <hive-jobq-graph>.refresh() (its own
// separate subscription).
rebuild_queue_changed: applyRebuildQueueChanged,
schedules_changed: applySchedulesChanged,
capabilities_changed: applyCapabilitiesChanged,
tool_groups_changed: applyToolGroupsChanged,
@ -329,7 +338,7 @@ window.marked = marked;
const es = openStream(
'/api/dashboard/stream?kinds=sent,approval_added,approval_resolved,' +
'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',
);
es.onmessage = (e) => {