inFlightOpsByAgent() read e.agent, a DAG-level field that no longer
exists (agent moved to per-node when DAGs became agent-per-node). So
the SW4RM tab's 'building...'/'meta-updating...' badges never matched
any real agent, and multi-agent DAGs (a startup sweep's MetaLock
cascade, a hive-wide restart) showed nothing at all on the per-agent
cards even while the rebuild queue clearly had them in flight.
Derive each agent's in-flight kind+state from its own node(s) within
the entry instead: a DAG can be 'running' overall while a given
agent's subgraph hasn't started (still queued behind an earlier node
in its chain), so per-node state is also more accurate than the old
per-DAG state for the badge, not just more available.
nodeComponents() split a DAG into weakly-connected components via deps
edges, but post #2476/#2450 every agent's rebuild subgraph hangs off a
shared MetaLock node via AfterOk, so the whole meta-update cascade is one
connected component and rendered as a single wall-of-chips line.
Add a second pass (splitFanOut) that further splits a component's
topo-ordered nodes on out-degree>1 points: a node with more than one
direct dependent renders as its own one-node line, and each dependent
becomes the root of an independent line. Purely deps-structure-driven,
same as the existing WCC split - no agent-field grouping involved. A
component with no fan-out (the common single-agent case) is unaffected.
Since the boot sweep (#2450) and meta-update cascade (#2476) became
single DAGs that grow subgraphs in-place, nothing constructs the old
fan-out anchors/parents anymore:
- NodeKind::Noop (the old boot_root grouping anchor) — no constructors.
- Template::StartupSweep / Source::StartupSweep (the old fan-out parent
template + cascade-child source) — replaced by Template::Boot and
Source::AutoUpdate/MetaUpdate respectively.
Drops the three variants + their as_str arms + the Noop executor arm, and
refreshes the stale fan-out/anchor doc comments (Boot/MetaUpdate/Source
docs, coordinator.md, dashboard.md). Frontend: the queue-kind glyph moves
from the dead startup_sweep to boot (which had none), and the dead
rqe-source-startup_sweep style is dropped.
No behaviour change — pure dead-variant removal.
Dashboard AGENT log tab sent unit=hive-ag3nt.service (the crate/dir
name) but the actual systemd unit is hive-agent.service, so every
fetch 400'd against the backend allow-list. Fixed the dropdown value
and, per the same issue's request, added the other per-agent daemons
(hive-mcp-http, hive-bash-daemon, hive-matrix-daemon) as selectable
units, plus hive-priv.service on the SYSTEM tab's host-daemon list.
Extended both backend allow-lists (post_journal / post_journal_host)
to match, and fixed a stale systemd.services.hive-ag3nt doc reference
in agent-hierarchy.md that had the same crate/unit-name confusion.
With the meta-update cascade (#2476) and startup sweep (#2450) folded
into single DAGs that grow per-agent subgraphs via append_subgraph,
nothing links parent/child DAGs anymore — parent_id is dead.
hive-c0re: drop parent_id from Dag/DagSpec (+ the DagView copy); delete
append_children and cancel_children (no callers); simplify trim_history
(no more terminal-parent-with-live-children guard — a one-big-DAG is
terminal only when its whole graph settles); drop the rebuild() parent_id
param; QueueDag returns just the polled DAG (no fan-out children to
gather). hive-sh4re: drop the DagView.parent_id wire field.
frontend: a multi-step op is one DAG now, so renderRebuildQueue drops the
childrenOf/orphans cross-DAG grouping and renders each entry flat; its
per-agent subgraphs render as nodes within the one row (split by deps).
Removed the dead rqe-child style + isChild plumbing.
Docs + the child-DAG queue tests updated/removed to match.
mara's review comment: the frontend shouldn't apply its own grouping
logic on top of the DAG — it should render the structure the backend
already provides. The actual structure is the nodes' deps graph, not
the incidental n.agent field.
Replace the group-by-agent heuristic with nodeComponents(): splits
entry.nodes into weakly-connected components via the deps edges
(undirected reachability), then topo-sorts each component (Kahn's
algorithm) so a chain renders in true dependency order. A DAG made of
independent per-agent subgraphs (no cross-agent deps) still comes back
as separate components — same visual result for today's templates —
but the split is now driven by what the backend actually encodes, and
naturally extends to any future non-agent-aligned branching. Agent
name is still shown as a per-component label, but purely as adjunct
info sourced from that component's own nodes, not the grouping key.
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.
Restart now offers the same graceful-vs-hard choice stop already has:
single-agent menu item and the bulk-select action bar both grow a
'restart gracefully' checkbox that routes through the existing
submit::graceful_restart DAG (signal -> drain -> stop -> reconcile)
instead of a hard restart. Backend gains a ?graceful=true query param
on POST /api/restart/<name>, mirroring post_kill's shape (renamed
KillParams -> GracefulParams since it's now shared).
- builds.js NODE_KIND_LABEL: 'set wanted' (and the previously-missing
'noop') so the new head node renders with a label, not the raw kind.
- docs/coordinator.md + templates.rs module doc: the power-op DAG shapes
now show the head SetWanted node; SetWanted added to the lease-needing
list with the atomicity rationale.
Review (iris/argus): the dashboard rebuild live-log header labels one
specific node's log stream (liveNode, keyed by its build_log_id), so it
should show that node's own agent — entryAgents(running) listed every
agent in the DAG, which would mislabel a single agent's log once DAGs
span multiple. The other entryAgents() sites (row label, cancel-confirm,
fingerprint) are correct whole-DAG summaries and unchanged.
DagView no longer has a DAG-level agent, so consumers derive it from the
per-node agents:
- hivectl dag_progress.rs: dag_agents(d) helper (distinct node agents,
comma-joined) in place of d.agent.
- dashboard builds.js: entryAgents(entry) helper likewise for the
rebuild-queue card + live-log header + cancel confirms.
- docs/coordinator.md: lease prose (node-agent-keyed, global per agent),
wire shape (NodeView.agent, no DagView.agent), and dropped the removed
dedup section.
Per mara's feedback on PR #2407 ("better: you can also provide url in
dashboard, same as with matrix, no host config"), drops
services.hyperhive.extraForges and the admin-API mint/revoke flow
entirely. The operator now creates a token on the external forge
themselves and pastes a label + base URL + access token into the
dashboard's FORGES tab, the same shape as the GitHub PAT flow plus the
base-URL field from the matrix extra-account flow. hive-c0re only ever
writes/deletes two local files per account (forge-<label>-token,
forge-<label>.json sidecar for the URL) via hive-priv — no remote
account creation, no admin token, no revoke-on-the-remote-side, no nix
config to enumerate.
- nix/host-modules/hive-forge/default.nix: removed the extraForges
option, its label-format assertion, and the HYPERHIVE_EXTRA_FORGES
env forwarding.
- hive-c0re/src/forge/extra.rs: deleted (REST admin-API provisioning,
no longer needed).
- hive-c0re/src/dashboard/extra_forges.rs: GET /api/extra-forges?
agent= lists an agent's stored forges by scanning its state dir
(mirrors matrix_accounts.rs's filename-scan listing), POST
/api/extra-forge-account (agent/label/base_url/token/
action=add|remove) stores or removes an account.
- hive-sh4re/priv_proto.rs + hive-priv/main.rs: new
WriteAgentExtraForgeAccount/DeleteAgentExtraForgeAccount priv
requests (adds base_url, writes/deletes a JSON sidecar alongside the
token).
- hive-c0re/src/priv_client.rs: matching wrapper functions.
- frontend/packages/dashboard/src/credentials.{html,js}: FORGES tab is
a per-agent list + add-account paste form (label/base_url/token), no
grant/revoke-from-catalog UI.
- docs/web-ui/dashboard.md: FORGES tab section rewritten.
Supersedes the design in PR #2407 (already approved+green on the old
admin-API model) — opening as a fresh PR against the same issues
rather than force-pushing over the approved one.
New POST /api/infra-container/{name}/{action} dashboard route (start/
stop/restart on hive-ci/hive-forge/hive-gateway/hive-matrix), reusing
the existing priv_client::control_infra_container helper the
infra_admin agent path already uses, plus an audit_log entry per
attempt. Adds infra_containers to the /api/state StateSnapshot (name +
live running status via systemctl is-active). New 1NFR4 sub-tab on the
C0R3 dashboard page: one row per infra container with a running/
stopped badge and start/stop/restart buttons, polled every 5s while
the sub-tab is open.
Part 2 of hyperhive#1970 (backend/CLI landed in PR #2378). Renames
/matrix-accounts.html -> /credentials.html and restructures it with a
sub-tab strip (MATRIX / GITHUB), reusing the shared @hive/shared/tabs.js
tab strip already used by /logs.html.
MATRIX tab carries over the existing account list + login form
unchanged. GITHUB tab adds a single-PAT provisioning form: status line
(present/absent, read from GET /api/github-account), a security-warning
banner (dedicated bot account + minimally-scoped token), a link to
generate a PAT at github.com/settings/tokens, and a paste-token form
posting to POST /api/github-account. Both tabs share one agent picker.
Updated docs/web-ui.md + docs/web-ui/dashboard.md to describe the new
page shape, and the H0M3 hub tile (index.html) to point at the renamed
page.
Both mdNode implementations (agent UI app.js, dashboard common.js)
assigned marked.parse() output straight to innerHTML with no
sanitizer. marked v5+ dropped its built-in sanitize option, and there
was no DOMPurify anywhere in frontend/, so markdown containing raw
HTML/script tags rendered live in the browser.
Both sinks receive untrusted input in practice: the agent UI's mdNode
renders recv tool_result bodies, assistant prose, and send/ask/answer
payloads sourced from peer agents and matrix-relayed messages (the
documented prompt-injection adversary); the dashboard's mdNode renders
agent-authored .md files served verbatim by GET /api/state-file
(the endpoint validates path, not content). Since the per-agent UI and
dashboard are same-origin behind the gateway with operator-authority
endpoints (approve/spawn/rebuild/destroy/answer-question), injected
script would run with the operator's session.
Fix: DOMPurify.sanitize() the marked.parse() output at both sinks
before assigning to innerHTML. Added dompurify as a dependency to
both the agent and dashboard npm workspaces, recomputed npmDepsHash
in nix/frontend.nix for the updated lockfile. Also corrected
docs/web-ui/shape.md, which claimed the markdown-rendering path was
XSS-safe by construction the same way the text-node-based linkify
path is — it isn't; it's safe because it's sanitized.
CSP hardening for the dashboard (no unsafe-inline) is a separate,
larger backend change (response headers in hive-c0re) and is left as
a fast-follow rather than folded into this fix.
- deploy-window gate (meta::exclusive) + path-limited meta commits:
a perm/lock/topology commit can no longer sweep an ApprovalDeploy's
staged flake.lock and neuter abort_deploy (regression test included)
- cancel surfaces now buffer terminal roll-ups the scheduler drains,
so a queued approval DAG cancelled by the operator resolves its
approval instead of dangling, and cancelled power ops revert their
wanted flip to the observed state
- hivectl restart / restart-all ride the queue (lease serialization,
transient guard) and restart sets wanted=Up like the old kill+start
- exactly one Rebuilt event per rebuild DAG, emitted at terminal
- StopForUpdate pre-seeds a missing agent_power row from the pre-stop
observation so a rebuild can't strand an unknown agent offline
- history trim keeps terminal fan-out parents with live children
- audit_log back on db::open; swarm.js badge for reconcile DAGs
each queue card now shows its DAG's node chain (per-node state, step,
build-log link), fixing 'queue jumps don't show on the dashboard'.
live-log panel keys off the running node's log. new
services.hyperhive.c0re.buildSlots option (default 1) threads the
concurrent nix-build count into serve.json.
Two operator-visible strings and one code comment still referenced 'the
manager' in the approval card UI:
- init_config card description: 'manager customises agent.nix before
spawn' -> 'submitting agent customises agent.nix before spawn'
- deny form data-prompt: 'sent to manager' -> 'sent to submitter'
(the deny note goes to the ApprovalResolved submitter, not a fixed
manager role)
- code comment: 'surfaced to the manager via' -> 'surfaced to the
submitting agent via'
- builds.html meta text: 'manager learns each outcome' -> 'the
submitting agent learns each outcome'
The 'manager' role is no longer structural — root-ness is topological.
Any agent with the approvals tool group can submit; the note/event routes
to the submitter.
The QueueKind enum has graceful_stop, start, and stop variants but the
dashboard QUEUE_KIND_GLYPH map only covered rebuild/meta_update/spawn/
destroy/restart/startup_sweep/perm_change — the three missing kinds
rendered as '?' in the build queue UI.
This became visible with the deferred start-after-rebuild change: a fast-lane
Start entry now appears as a child of its Rebuild parent, but showed the
fallback '?' glyph.
Glyphs assigned: graceful_stop=⏹, start=▶, stop=■.
Also update the dashboard.md kind-glyph list to include all nine kinds and
note that deferred start-after-rebuild entries also use parent_id grouping.
fix(dashboard): update 'view queue' link to /builds.html
The build queue moved to its own /builds.html page. The queue-summary
'view queue →' link in tabs.js still pointed at /core.html, so clicking
it landed on the wrong page.
fix(broker): filter agent inbox to unread (acked_at IS NULL)
recent_for was returning all messages regardless of ack state, so the
agent inbox showed everything even after 'mark all read'. Now filters
to acked_at IS NULL — mirroring exactly what mark_all_read drains —
so the inbox empties on reload after the operator drains it.
Remove or shorten comment blocks that:
- explain where code used to live (impl history)
- duplicate rationale already in docs/web-ui.md
- contain speculative/future-work notes
Changes:
- tabs.js: drop "SYST3M tab used to render this" history; keep badge purpose
- schedules.js: drop "used to be a card layout" history + speculative nit note
- stream-worker.js: replace 10-line problem-statement with docs pointer
(rationale already in docs/web-ui.md SSE multiplexing section); trim
future-work parenthetical from subscription-tracking comment
- system-sections.css: drop extraction-history prose; keep what it does
- core.css: drop move-history prose; keep what it imports and why
Part of issue cleanup per operator feedback.
New standalone page /builds.html consolidating rebuild queue, meta
inputs, and build log history into one place, with a new 'Builds' home
tile linking to it. Addresses mara's request: new sub-page with a new
home tile, all three items moved there.
Changes:
- builds.html: new page with three sub-tabs: R3BU1LD QU3U3, M3T4 1NPUTS,
BUILD L0GS. Same minimal-chrome header + createTabStrip pattern as core
and logs pages.
- builds.js: new bundle combining rebuild queue renderer (from core.js),
meta inputs renderer (from core.js), rebuild-live-log renderer (from
core.js), and build log history renderer (from logs.js). Deep-links to
/builds.html?id=N#buildlogs. Count pill id: builds-tab-count-rebuild.
BUILD L0GS tab lazy-loads on first activation.
- builds.css: @imports system-sections.css (rebuild queue + meta inputs +
live-log styles) and logs.css (build-logs-* component styles).
- build.mjs: register builds.js, builds.css, builds.html.
- core.html: remove R3BU1LD QU3U3 + M3T4 1NPUTS tabs (now on builds.html).
Default tab changes to K3PT ST4T3.
- core.js: remove renderMetaInputs, renderRebuildQueue + helpers,
renderRebuildLiveLog + live-log state, elapsed-time tickers,
updateRebuildCount, and the rebuild_queue/meta_inputs SSE handlers.
Remove openBuildLogStream + util imports no longer needed.
- core.css: remove .rebuild-live-log-* rules (moved to system-sections.css
so builds.css can share them via @import).
- system-sections.css: add .rebuild-live-log-* styles (moved from core.css);
update comment to mention builds.html.
- logs.html: remove BUILD tab + pane (moved to builds.html).
- logs.js: remove fetchBuild(), fmtTs, fmtDuration, openBuildLogStream
import, and rebuild_queue_changed SSE debounce. Default tab: 'agent'.
SSE stream retained for audit_entry_added live-appends.
- index.html: add Builds tile (🔨, rebuild queue · meta inputs · build
logs); update Core tile desc to 'kept state · container load'; update
Logs tile desc to remove 'build'.
Add GET /api/permissions/stale endpoint that returns agent names with
explicit capability/tool-group JSON entries but no live container AND
no kept-state tombstone. Ghost detection is now entirely server-side —
one authoritative call, no client-side roster cache, no staleness window.
The previous client-side approach in core.js made three parallel API
calls (GET /api/capabilities, GET /api/tool-groups, GET /api/state) and
filtered the result against a module-level `liveContainerNames` Set
populated only on cold load and form submits. Any container lifecycle
event (spawn, destroy) while core.html was open left `liveContainerNames`
stale, risking a false-positive ghost entry for a live container.
Changes:
- permissions.rs: add `get_stale_permissions` handler + `StalePermsResponse`
struct. Computes live roster (containers_snapshot), tombstone set
(Coordinator::kept_state_names), explicit perm names (capabilities::read
+ tool_groups::read), then returns the difference sorted.
- dashboard.rs: register GET /api/permissions/stale.
- core.js: replace the three-call client-side logic in
`fetchAndRenderStalePerms` with a single fetch to /api/permissions/stale.
Remove `liveContainerNames` state + its syncFromSnapshot population.
Adds a "stale permission entries" sub-section within the K3PT ST4T3 pane
on /core.html showing agents with explicit capability/tool-group JSON
entries but no live container — typically renamed or manually-deleted agents
whose entries persisted (e.g. the old "root" manager name after rename to
"ruth").
Each ghost agent gets a "✕ clear perms" button that calls
DELETE /api/permissions/{agent} (added in the prior commit). Lazy-loaded
on first K3PT ST4T3 tab activation; auto-refreshes on capabilities_changed
and tool_groups_changed SSE events.
core.js: track liveContainerNames from /api/state.containers; add
renderStalePerms + fetchAndRenderStalePerms; hook tab onShow + SSE handlers.
system-sections.css: new .tombstones-stale-* selectors for the ghost list
rows and error message.
core.html: add #tombstones-stale-perms div inside the K3PT ST4T3 pane;
expand comment to describe both sub-sections.
Read the persisted model name from each agent's harness state file
(harness/hyperhive-model) and surface it as a small blue badge on
the container row in the SW4RM tab.
- container_view.rs: add `active_model: Option<String>` to
ContainerView; populated by new `read_active_model` helper that
reads harness/hyperhive-model; only set when container is running
(stale model info from a stopped agent is misleading)
- container_view.rs: add active_model to ContainerView literal in
host_stats test helper
- tabs.js: render badge-model chip after needs-update, before
reminders; add active_model to the row fingerprint so re-renders
fire on model change
- common.css: add .badge-model (blue, 80% opacity — informational)
Adds pause/resume support for scheduled prompts.
Backend:
- New paused_at_unix column on scheduled_prompts table (added via
ALTER TABLE migration so existing databases are upgraded on first
start). The due-rows index is dropped and recreated to also exclude
paused rows so the worker never fires them while paused.
- Worker's due() query gains AND paused_at_unix IS NULL filter.
- New pause(id) and resume(id) methods on ScheduledPrompts; both are
idempotent and refuse cancelled rows.
- New POST /api/schedules/{id}/pause and /api/schedules/{id}/resume
dashboard endpoints (operator-direct, no approval gate). Both emit
a schedules snapshot on success so the tab updates live.
- WireSchedule gains paused_at_unix: Option<i64> so the frontend can
render the state without an extra fetch.
Frontend:
- Paused rows render with a distinct row class + muted opacity.
- The next-fire cell shows a yellow pause glyph + tooltip with the
paused-since timestamp and the would-have-fired time.
- Actions column: pause/resume toggle button (⏸/▶) beside fire/edit/cancel.
Fire-now is disabled while paused (resume first).
- Sort order: active → paused → cancelled (paused slot keeps schedules
visible without mixing them into the active top section).
- pauseSchedule() / resumeSchedule() async functions POST to the new
endpoints and refresh the table on success.
Both remove_agent() calls now run unconditionally for maximum partial
cleanup, but any I/O error is returned as HTTP 500 instead of silently
200-ing — so the frontend's !resp.ok path fires and the operator sees a
meaningful error rather than the stale row reappearing unchanged.
Also add a clarifying comment on isStale in permissions.js explaining
that containersState is keyed from nixos-container list (which includes
stopped-but-configured containers), so a temporarily-stopped agent is
not treated as stale — only destroyed/renamed agents are absent.
The P3RM1SS10NS tab showed agents that no longer exist in the live
container roster — e.g. an agent named 'root' that was renamed or
destroyed but still had explicit entries in tool-groups.json and/or
capabilities.json. The roster-union behaviour is intentional for
temporarily-stopped agents, but stale entries from renamed/destroyed
agents are confusing.
Backend (dashboard/permissions.rs):
- New DELETE /api/permissions/{agent} handler that bypasses the live-
roster guard (intentionally — that's the point). Calls
tool_groups::remove_agent + capabilities::remove_agent to clear both
JSON files, then emits live SSE snapshots so the tab updates without
a page reload. Format-checks the agent name but does not require it to
be in the containers snapshot.
Frontend (permissions.js):
- renderCapabilities / renderToolGroups now cross-reference agentNames
against containersState (the live roster, already imported). Agents
not in the live roster get an isStale flag.
- Stale rows get a '(not running)' label and a '✕ remove' button that
calls clearStaleAgent() — a new async helper that DELETEs the stale
entry and re-fetches both perm tables.
- Non-stale agents without explicit assignments still get '(default)'.
CSS (dashboard.css):
- .perm-row-stale (reduced opacity), .perm-stale-label (muted small
text), .perm-remove-btn (small red-bordered button) + disabled state.
The CSS for the rebuild-queue live-log panel targets .rebuild-live-log
(border, border-radius, margin-top, background) but the HTML element only
had id="rebuild-live-log" — no class. As a result the panel box styles
never applied and the live log rendered unstyled (no border, no background,
no visual separation from the queue rows).
Fix: add class="rebuild-live-log" to the element so the CSS selector
matches.
Both kinds fell through to the spawn branch in renderApprovals, showing
a misleading 'spawn' chip and agent-spawn body text. Mara saw a meta-input
bump render as a spawn card for agent damocles and denied it.
Backend (dashboard.rs):
- Add commit_ref: None to the MergeConfigPr arm (struct was incomplete).
All arms of ApprovalView now initialise every field.
Frontend (call.js):
- Add isUpdateMeta / isSchedule booleans alongside the existing kind flags.
- Glyph: update_meta_inputs gets ↻, schedule_prompt gets ⏱.
- Kind chip: 'meta-update' / 'schedule' (no kind-spawn class for either).
- Body: update_meta_inputs parses commit_ref as JSON Vec<String> and shows
'bump flake inputs: foo, bar' or 'bump all flake inputs'; schedule_prompt
parses SchedulePromptPayload and shows targets + first-fire time + cadence
+ a truncated body excerpt.
- History row: add 'meta-update' and 'schedule' cases (were both 'spawn').
- Import fmtDuration from util.js (needed for schedule cadence display).
Completes the merge_config_pr approval-card link for live-added approvals.
The ApprovalAdded SSE event now carries pr_number (only for
merge_config_pr); applyApprovalAdded was dropping it, so a live-added
merge_config_pr card showed the sha but not the 'review PR on forge' link
until a cold /api/state reload. Carry pr_number through the same way as
sha_short.
Follow-up to the destroy-guard removal — the bootstrap/root container is
now destroyable end-to-end. The frontend already offered DESTR0Y/PURG3
for every container; the only manager-gating left was a stale doc-comment
('destroy/purge hidden for the manager') and a vestigial bare block
wrapping the menu appends. Drop both (no behaviour change), and update the
dashboard.md menu doc: 'disabled for the bootstrap container' -> available
for any container (hive-c0re recreates it on next startup if destroyed).
renderApprovals had no merge_config_pr case, so those approvals
mis-rendered as generic spawn cards (spawn chip, no sha, 'container will
be created' body). Add a dedicated branch:
- distinct glyph + 'merge-pr' kind chip
- show the reviewed PR-head sha (sha_short), like apply_commit
- a 'review PR on forge' link built from pr_number (now on ApprovalView),
gated on forge_present, mirroring the apply_commit 'commit on forge' link
- no config-diff side-panel (apply_commit-only for now)
History rows get the 'merge-pr' chip too.
Adds a Window::All option to the per-agent stats page and the hive-wide
rollup, selectable as a new 'all' tab on both.
- hive-ag3nt (per-agent, time-bucketed): All ranges from MIN(started_at)
to now (fallback to now on an empty table) with an adaptive bucket
width laddered by span — hourly <=2d, daily <=90d, weekly <=2y, 30-day
beyond — so the trend series stays bounded (~<=104 buckets) at any age.
- hive-c0re (swarm rollup, not time-bucketed): All sets from=0 so the
aggregate covers every recorded turn across all agents.
- frontend: an 'all' button on both the agent stats and dashboard hive
stats window selectors (createTabStrip + fetch already pass the window
string through, so no JS change needed).
cargo check passes on both crates; FE builds clean. Fixes#1919.
The kill endpoint deserializes ?graceful as a strict bool (true/false),
so the dashboard's ?graceful=1 failed with 'provided string was not
true or false' and graceful shutdown silently broke. The stop and
bulk-stop buttons build the param at two call sites in tabs.js; send
graceful=true instead (a hard kill still omits the param entirely).
Keeps the API contract strict — the FE one-liner damocles and I agreed
on, superseding the lax-bool deserializer in the closed#1917.
Fixes#1914.
The themed dialog component (modal.js: themedConfirm / themedPrompt /
themedToast) is raised from common.js's data-async / data-confirm
handler, which every page loads — but its .tc-* styles lived only in
dashboard.css (the main tabbed dashboard's stylesheet). So on any
standalone page (core/C0R3, settings, flow, logs) a dialog rendered
completely unstyled: the build-queue rebuild-cancel confirm came up as
raw text.
Extract the .tc-* rules into a dedicated modal.css component (paired
with modal.js) and @import it from common.css, so the styles load
wherever a dialog can fire — not just the main dashboard. esbuild
inlines the @import into the common.css bundle, so there's no extra
request and the main dashboard is visually unchanged (it loads
common.css too). The stale 'themedConfirm() in common.js' comment is
gone with the move. Fixes#1910.
renderReminders() calls appendLinkified() (for the reminder file_path
and message bodies) but schedules.js only imported { $, el } from
common.js, so the reminders_changed SSE handler threw
'ReferenceError: appendLinkified is not defined' and the reminders
pane failed to render. appendLinkified is exported from common.js;
add it to the import. Fixes#1909.