docs: fix genuine passive-voice hits in docs/web-ui/dashboard.md (part 1/2)

First half (lines 1-697) of dashboard.md's write-good.Passive backlog --
31 of the file's 70 hits rewritten to name an actor (hive-c0re, the page,
the operator, the table, the filter, builds.js, Provision, the matrix
daemon, ...), reusing an actor already named nearby wherever one exists.
7 left alone in this half: a fixed idiom ("not to be confused with"),
two false positives ("named buckets" / compound adjective; a
field-is-set data-state conditional), a negative-capability idiom
("nothing is written"), and two established state-descriptor idioms
("is unchanged", "is tooltipped").

Part 2 (lines 762-1389, ~32 more hits) is a separate follow-up, not
started -- dashboard.md is too big for one PR.
This commit is contained in:
iris 2026-09-08 15:10:01 +02:00 committed by mara
commit a52d344600

View file

@ -4,7 +4,7 @@
> [Shape (shared)](shape.md) · [Per-agent page](agent.md)
The dashboard is served at `/dashboard.html` (with the home page at `/`).
hive-c0re serves the dashboard at `/dashboard.html` (with the home page at `/`).
It has a fixed chrome header at the top and a `<main>` that shows exactly
one tab pane at a time. The URL hash (`#swarm`, `#call`, `#system`,
`#permissions`, `#schedules`) drives which pane is
@ -42,13 +42,13 @@ from the dashboard tab strip.
snapshot. Hidden when there are no warnings.
- **Browser tab title**`hyperhive // h1ve-c0re` by default; updated
to `<swarm> / <hive> // h1ve-c0re` once `hive_name` / `swarm_name`
arrive in the state snapshot. When there are pending approvals, a
`(N)` prefix is prepended `(3) pr1ma // h1ve-c0re` — so the
arrive in the state snapshot. When there are pending approvals, the
page prepends a `(N)` prefix — `(3) pr1ma // h1ve-c0re` — so the
operator can see the call count in an unfocused browser tab without
opening the dashboard. The prefix is set on the initial `/api/state`
cold-load and updated live by `approval_added` / `approval_resolved`
SSE events; it's preserved when `hive_name` / `swarm_name` later
replace the raw title.
opening the dashboard. The page sets the prefix on the initial
`/api/state` cold-load and updates it live via `approval_added` /
`approval_resolved` SSE events; it's preserved when `hive_name` /
`swarm_name` later replace the raw title.
The FL0W and L0GS pages use a slim header (a `← home` back-link + the
page title) rather than the dashboard tab strip — they're standalone
@ -61,13 +61,13 @@ tree using `ContainerView.parent` (populated by
`hive-c0re/src/agent_config/topology.rs` — not to be confused with
`hive-c0re/src/dashboard/topology.rs`, which only holds the
set-parent endpoints).
Each container's row is prefixed with ASCII tree glyphs (`├─`,
`└─`, `│ ` continuation columns) showing the agent
parent/child hierarchy. When every container has `parent = null`
ASCII tree glyphs (`├─`, `└─`, `│ ` continuation columns) prefix
each container's row, showing the agent parent/child hierarchy.
When every container has `parent = null`
(flat topology) the tree collapses to a plain list with no
glyphs. Children are sorted alphabetically within each parent;
roots likewise. Cycles in the parent graph are tolerated
orphaned containers (not reachable from any root) are appended
glyphs. The tree sorts children alphabetically within each parent;
roots likewise. The tree tolerates cycles in the parent graph
it appends orphaned containers (not reachable from any root)
as roots so no agent disappears. Pulsing red banner at the top
of this section if any two sub-agents hash to the same port
(`port_conflicts` from `/api/state`): the operator must rename
@ -138,12 +138,12 @@ page — see below.)
claude-creds badge). One action: `PURG3` (wipes state + applied
dirs; `POST /api/purge-tombstone/{name}`).
⚠️ **Not only *destroyed* agents.** Nothing records a destroy: every
definition-side artifact (state subvolume, proposed + applied repos,
meta registration, topology entry) is written by `Provision` *before*
the container exists and survives `lifecycle::destroy`. An agent
⚠️ **Not only *destroyed* agents.** Nothing records a destroy: `Provision`
writes every definition-side artifact (state subvolume, proposed + applied
repos, meta registration, topology entry) *before* the container exists,
and that artifact set survives `lifecycle::destroy`. An agent
part-way through a spawn is byte-identical on disk to a tombstone, and
both are listed. The pane carries a standing warning to that effect, and
the pane lists both. The pane carries a standing warning to that effect, and
the row badge says `offline` rather than `destroyed` — the absence of a
container is all the backend can actually prove. Fixing it properly
needs a recorded destroy: **#3020**, deferred to the swarm-controller /
@ -169,8 +169,8 @@ files read-only (world-readable; no `hive-priv`) and skips agents whose
scope dir is absent (= not running). Pull-only: `core.js` polls every
5 s **only while the C0NT41N3R L04D sub-tab is active** (CPU needs a fresh
sample each refresh), and stops on sub-tab change. Disk size (`disk_bytes`)
rides the same row but is fed by a separate ~5 min background `du` sampler
(state dir + container writable rootfs, shared nix store excluded via
rides the same row, but a separate ~5 min background `du` sampler feeds
it (state dir + container writable rootfs, shared nix store excluded via
`du -x`), so the 5 s poll stays cheap cgroup-only reads; the row carries the
last-sampled value (`null` until the first sample). Network is intentionally
omitted — agents share the host netns, so there is no per-container net
@ -231,8 +231,8 @@ calls the mount's `onCancel(id)` callback, and the page does the actual
own domain concept (the component knows nothing about that endpoint).
**Rows carry no source chip, kind label, timing, or build-log
deep-link** — the generic graph wire doesn't carry those fields, and
rows are meant to present exactly what the endpoint provides rather
than reconstruct chrome the backend no longer sends. Settled entries
rows deliberately present exactly what the endpoint provides rather
than reconstructing chrome the backend no longer sends. Settled entries
render their **full step tree**, not just a bare summary — the wire
doesn't filter `Done` nodes out.
@ -243,12 +243,12 @@ operator narrow which root groups render; unchecking a state re-fetches
hiding rows client-side, so the `onUpdate` callback's node list (and
everything downstream of it — the count pill, the live-log panel) only
ever sees what's actually shown. Filtering is by a **root's own**
state, which is already its subtree's rolled-up answer, so a group is
kept or dropped whole, never split mid-tree. Default selection is
state, which is already its subtree's rolled-up answer, so the filter
keeps or drops a whole group, never splitting mid-tree. Default selection is
every state **except** `Done`/`Skipped` — a fresh queue view leads
with what's still moving or needs attention, not the settled tail; the
`states` param is omitted entirely (identical request to before this
filter existed) when every state is checked. Server-side: the query
with what's still moving or needs attention, not the settled tail;
builds.js omits the `states` param entirely (identical request to
before this filter existed) when every state is checked. Server-side: the query
narrows [`Queue::graph_snapshot`]'s already-bounded (`MAX_HISTORY_DAGS`)
root set — the history cap and the state filter are independent
concerns, so a narrow filter never reaches further back in time to
@ -274,14 +274,14 @@ selectively `nix flake update`, rendered as an indented tree:
every fetched input at every depth (`hyperhive`,
`hyperhive/nixpkgs`, `agent-<n>`, `agent-<n>/mcp-<x>`, …), each
shown once at its shallowest path. `read_meta_inputs` walks the
lock graph with a `visited` set `follows` aliases and rev-less
nodes are skipped. A `select all / select none`
lock graph with a `visited` set, skipping `follows` aliases and rev-less
nodes. A `select all / select none`
control sits above the tree. Checking inputs + submitting bumps
the lock in `/meta/` and rebuilds the selected agents in
sequence; each outcome reaches the root agent as a `rebuilt`
system event. `POST /meta-update`. While a lock-bump ripple runs,
the panel shows a pulsing "⏳ meta-update running" banner and the
update button is disabled (snapshot field `meta_update_running`,
the panel shows a pulsing "⏳ meta-update running" banner and disables
the update button (snapshot field `meta_update_running`,
live event `meta_update_running`).
**BUILD L0GS** — all-agents build log history (moved from `/logs.html`).
@ -318,9 +318,9 @@ An agent picker (populated from `state.containers`, the live roster) drives a li
agent's accounts — name, homeserver, user id, and a status dot —
read from `GET /api/matrix-accounts?agent=<name>`
`{ accounts: [ { name, homeserver, token_present, live, user_id } ], as_of_unix }`.
`token_present` is whether a token is **stored**; `live`, `homeserver`,
and `user_id` are backfilled from the matrix daemon's
`matrix-accounts.json` snapshot — a host-visible file the daemon
`token_present` is whether a token is **stored**; the matrix daemon
backfills `live`, `homeserver`, and `user_id` from its
`matrix-accounts.json` snapshot — a host-visible file it
**force-rewrites every ~30s** (a heartbeat), so `as_of_unix` (the
snapshot mtime) advances while the daemon is alive and a *stalled* value
genuinely means "stopped publishing," not just "old snapshot." An account
@ -357,7 +357,7 @@ privileged write path as the hive-internal `matrix-token`; the token is
**never** echoed back, and the page clears the secret inputs on submit
regardless of outcome. The account list reflects what's *provisioned*
(an account with a stored token), so a config-declared-but-unprovisioned
account appears only once it has been provisioned through the form.
account appears only once the operator provisions it through the form.
### GITHUB tab
@ -445,19 +445,19 @@ The current capabilities are:
Each row is one agent. Columns are the capability names returned by
`GET /api/capabilities` as `caps: Vec<String>`. Checking or unchecking
boxes only stages the change in-browser; nothing is written until the
page-level **save all** button (described below) is selected. Row
operator selects the page-level **save all** button (described below). Row
values follow the `effective`/`assignments` rule described above.
**T00L GR0UPS** — per-agent tool-group permissions. Tool groups are
named buckets of MCP tools; each agent starts with a role default
(sub-agents: `messaging`, `meta`, `inbox`, `execution`
`ToolGroup::AGENT_DEFAULT`; root agent is seeded to
`ToolGroup::AGENT_DEFAULT`; hive-c0re seeds the root agent to
`ToolGroup::MANAGER_DEFAULT``messaging`, `meta`, `inbox`,
`lifecycle`, `approvals`, `scheduling`, `diagnostics`, `execution`,
that is every group except `forge` and `web_tools`). Checking /
unchecking stages which groups are active for the agent; the
page-level **save all** button (below) commits it. Columns come from
`GET /api/tool-groups`. A rebuild is queued so `HIVE_TOOL_GROUPS`
`GET /api/tool-groups`. hive-c0re queues a rebuild so `HIVE_TOOL_GROUPS`
takes effect.
The current tool groups are: `messaging`, `meta`, `inbox`, `lifecycle`,
@ -479,9 +479,10 @@ buttons exist. A single page-level `.perm-save-bar` with a **save all (N
agents)** button sits at the bottom of the pane, enabled only when some
checkbox diverges from its baseline. Selecting it diffs every checkbox
across *both* matrices and POSTs one batch to `POST /api/permissions` as
`{ changes: [ { agent, tool_groups?, capabilities? } ] }` — only the
perm-types that actually changed for each agent are included (an omitted
field leaves that file untouched; an included array fully replaces it).
`{ changes: [ { agent, tool_groups?, capabilities? } ] }` — the batch
includes only the perm-types that actually changed for each agent (an
omitted field leaves that file untouched; an included array fully
replaces it).
The backend coalesces an agent's capabilities + tool-groups into a
single rebuild, so changing both for one agent is one rebuild, not two.
The batch is atomic: it validates every change first and on any error
@ -493,8 +494,8 @@ isn't clobbered.
## SCH3DUL3S tab
Anything that fires at a future time. Operator-set schedules
are created inline in the table (last row); agent self-paced
Anything that fires at a future time. The operator creates schedules
inline in the table (last row); agent self-paced
reminders surface at the bottom as a sibling list — they
share enough conceptual ground to live together.
@ -519,13 +520,13 @@ headers tilt -45° via CSS so each column reads as a narrow
Per-schedule action column:
- **`↯ fire now`** — sends an out-of-band manual pulse to every
active target (recurring schedules keep their cadence; one-shots
are consumed after the manual fire). Disabled for paused schedules.
active target (recurring schedules keep their cadence; the manual
fire consumes one-shots). Disabled for paused schedules.
- **`⏸` / `▶` pause/resume toggle** — only on recurring schedules
(one-shots may also be paused to delay a one-time fire, so the
button always appears). Paused rows show a `⏸ paused` badge in the
`next` column and are sorted to the middle bucket (after active,
before cancelled). A paused schedule doesn't fire regardless of
(the operator may also pause one-shots to delay a one-time fire, so
the button always appears). Paused rows show a `⏸ paused` badge in
the `next` column; the table sorts them to the middle bucket (after
active, before cancelled). A paused schedule doesn't fire regardless of
`next_fire_at`; resuming (`▶`) clears the pause without adjusting
the next-fire time. POSTs to `/api/schedules/{id}/pause` or
`/api/schedules/{id}/resume`.
@ -598,15 +599,15 @@ Backed by `GET /api/stats-hive?window=<w>` in `hive-c0re`
`Coordinator::kept_state_names()` it opens
`agent_harness_dir(name)/hyperhive-turn-stats.sqlite` read-only (with a
500 ms `busy_timeout`, since `turn_stats` is rollback-journal) and rolls
the rows up — missing / unreadable / zero-turn dbs are skipped so one
the rows up, skipping missing / unreadable / zero-turn dbs so one
bad db never fails the endpoint. This is a **pull** surface (no SSE):
the data is fetched on page load and on window change. Rendered
the page fetches the data on page load and on window change. Rendered
with plain tables + CSS bars — the dashboard bundle ships no chart
library.
The cost figure is a deliberately rough estimate from a per-model
price table (`est_cost_usd`); it drifts with list pricing and is
labelled accordingly. The table is operator-tunable via the
price table (`est_cost_usd`); it drifts with list pricing and the page
labels it accordingly. The table is operator-tunable via the
`services.hyperhive.c0re.modelPrices` nix option — each key is a
model-family short name (matched case-insensitively as a substring of
the model id, longest match wins) mapping to
@ -658,8 +659,8 @@ Cold load backfills the last ~200 messages from `/api/dashboard/history`;
live frames arrive on `/api/dashboard/stream`. Each row is one broker
event — `sent` or `delivered` — with `from → to: body`. When a `sent`
and `delivered` event for the same message arrive within 3 seconds
(immediate delivery to a live recipient), the row is upgraded in place
(arrow becomes green ✓, title reads "sent + delivered") instead of
(immediate delivery to a live recipient), the page upgrades the row in
place (arrow becomes green ✓, title reads "sent + delivered") instead of
rendering two near-identical lines — genuine delivery latency (recipient
was busy) still appears as a second row. Each row carries `data-from` /
`data-to` attributes; an **agent filter select in the FL0W header** narrows
@ -680,7 +681,7 @@ stream sits a terminal-style compose box: `@name` picks the recipient
(sticky via localStorage; autocomplete from the live container list,
Tab/Enter to confirm; `@*` broadcasts). `POST /api/op-send` drops
`{from:"operator", to, body}` into the broker; the resulting SSE frame
re-renders the terminal row. The root agent is addressed as `@root`.
re-renders the terminal row. The operator addresses the root agent as `@root`.
## H0M3 page (`/`)
@ -693,8 +694,8 @@ from the shared theme (Catppuccin Mocha via `common.css` + `theme.css`).
`home.js` fills the swarm/hive identity line at the top. All dashboard
sub-pages include a `← Home` back-link for navigation.
No Matrix or Forge tile here — those surfaces (`/matrix/`, the forge's own
public URL) are reached directly, not linked from the dashboard UI.
No Matrix or Forge tile here — the operator reaches those surfaces
(`/matrix/`, the forge's own public URL) directly, not via the dashboard UI.
## L0GS page (`/logs.html`)