docs(web-ui/shape): fix stale CSS bundle count, SSE route prefix, bind-retry cap claims

Verified against frontend/packages/dashboard/build.mjs, hive-c0re/src/dashboard/mod.rs,
and hive-agent/src/web_ui/mod.rs:
- dashboard ships 9 page CSS bundles + colors/theme/common, not 4
- dashboard SSE routes are /api/dashboard/stream + /api/dashboard/history, not
  /dashboard/stream + /dashboard/history
- per-agent bind_with_retry has a MAX_BIND_ATTEMPTS=12 cap (gives up), unlike the
  dashboard's genuinely uncapped retry loop
- assets/swarm.js, assets/call.js -> swarm.js, call.js (no assets/ dir exists)
This commit is contained in:
iris 2026-08-15 12:22:52 +02:00 committed by mara
commit 26b55f34d3

View file

@ -16,10 +16,14 @@
- `GET /static/*` → bundled CSS + JS produced by esbuild - `GET /static/*` → bundled CSS + JS produced by esbuild
(`frontend/packages/{dashboard,agent}/build.mjs`). Both pages (`frontend/packages/{dashboard,agent}/build.mjs`). Both pages
pull the shared terminal pane + Catppuccin palette + typography pull the shared terminal pane + Catppuccin palette + typography
from `@hive/shared` (was `hive-fr0nt`); the dashboard ships four from `@hive/shared` (was `hive-fr0nt`); the dashboard ships one
CSS bundles (`common.css` loaded by every page, plus per-page CSS bundle per page (`colors.css` + `theme.css` + `common.css`,
`dashboard.css` / `flow.css` / `logs.css`); `common.css` inlines loaded by every page, plus a page-specific bundle —
`base.css` + `terminal.css` via esbuild's `@import` resolution. `dashboard.css` / `flow.css` / `logs.css` / `home.css` /
`settings.css` / `stats.css` / `core.css` / `builds.css` /
`credentials.css`); `common.css` inlines `@hive/shared`'s
`base.css` + `terminal.css` + `tabs.css` + `chrome.css` +
`pill.css` via esbuild's `@import` resolution.
`terminal.js` exports `{ create, linkify }` as ES module `terminal.js` exports `{ create, linkify }` as ES module
members; a back-compat shim in the IIFE bodies still exposes members; a back-compat shim in the IIFE bodies still exposes
a `window.HiveTerminal` global for callers that need it. The dashboard's a `window.HiveTerminal` global for callers that need it. The dashboard's
@ -42,14 +46,14 @@
high-water mark at the moment the snapshot was assembled); high-water mark at the moment the snapshot was assembled);
clients use it to dedupe their buffered SSE traffic against clients use it to dedupe their buffered SSE traffic against
the snapshot (drop frames with `seq <= snapshot.seq`). the snapshot (drop frames with `seq <= snapshot.seq`).
- `GET /dashboard/stream` (dashboard) / `GET /events/stream` - `GET /api/dashboard/stream` (dashboard) / `GET /events/stream`
(per-agent) → `text/event-stream` SSE for live updates. The (per-agent) → `text/event-stream` SSE for live updates. The
dashboard stream carries broker `Sent` / `Delivered` (mirrored dashboard stream carries broker `Sent` / `Delivered` (mirrored
by a forwarder task from the broker's intra-process channel) by a forwarder task from the broker's intra-process channel)
plus mutation events (`approval_added` / `approval_resolved`, plus mutation events (`approval_added` / `approval_resolved`,
`question_added` / `question_resolved`, `transient_set` / `question_added` / `question_resolved`, `transient_set` /
`transient_cleared`). Each frame carries a `seq`. The `transient_cleared`). Each frame carries a `seq`. The
matching backfill endpoint is `GET /dashboard/history` (last matching backfill endpoint is `GET /api/dashboard/history` (last
~200 broker messages wrapped in `{ seq, events }`) on the ~200 broker messages wrapped in `{ seq, events }`) on the
dashboard and `GET /events/history` (last 2000 `LiveEvent`s dashboard and `GET /events/history` (last 2000 `LiveEvent`s
also wrapped in `{ seq, events }`) on the agent. also wrapped in `{ seq, events }`) on the agent.
@ -201,7 +205,7 @@ re-render), call `refreshState()`. State shapes live in
`hive-c0re/src/dashboard/state_snapshot.rs::StateSnapshot` and `hive-c0re/src/dashboard/state_snapshot.rs::StateSnapshot` and
`web_ui/state.rs::StateSnapshot` — when `web_ui/state.rs::StateSnapshot` — when
adding state fields, plumb through the snapshot struct and the adding state fields, plumb through the snapshot struct and the
relevant domain module (`assets/swarm.js`, `assets/call.js`, etc.) render function. relevant domain module (`swarm.js`, `call.js`, etc.) render function.
**Focus preservation:** `refreshState` checks whether **Focus preservation:** `refreshState` checks whether
`document.activeElement` sits inside one of the managed sections `document.activeElement` sits inside one of the managed sections
@ -277,18 +281,20 @@ previews are type-aware:
Both bind their TCP listener with `SO_REUSEADDR` via Both bind their TCP listener with `SO_REUSEADDR` via
`tokio::net::TcpSocket` plus a retry loop on `AddrInUse` `tokio::net::TcpSocket` plus a retry loop on `AddrInUse`
(exponential backoff capped at 2s, **no attempt cap**) so an nspawn (exponential backoff capped at 2s) so an nspawn restart that races
restart that races the previous process's socket release resolves the previous process's socket release resolves itself, but the two
itself. The retry is uncapped on purpose: a capped budget once binaries differ on the attempt budget. The dashboard's
left the harness silently UI-less for the rest of its lifetime `bind_with_retry` (`hive-c0re/src/dashboard/mod.rs`) has **no
when a back-to-back restart held the port longer than the cap attempt cap** — retrying forever is deliberate, since genuine port
allowed. Genuine port collisions are preflighted host-side collisions are preflighted host-side (`lifecycle::{spawn,rebuild}`
(`lifecycle::{spawn,rebuild}` refuses with a clear error, refuses with a clear error, surfaced on the dashboard as a banner),
surfaced on the dashboard as a banner), so at this layer a so at this layer a persistent `AddrInUse` always reflects a
persistent `AddrInUse` always reflects a recoverable stale recoverable stale socket. Its first 12 attempts log at WARN; after
socket — retrying forever is the safe choice. The first 12 that the level drops to INFO so a long-held stale socket doesn't
attempts log at WARN; after that the level drops to INFO so a flood the journal. The per-agent UI's `bind_with_retry`
long-held stale socket doesn't flood the journal. (`hive-agent/src/web_ui/mod.rs`) instead gives up after
`MAX_BIND_ATTEMPTS` (12) and returns the `AddrInUse` error rather
than looping forever.
The per-agent UI optionally binds a `UnixListener` instead of The per-agent UI optionally binds a `UnixListener` instead of
TCP when `HIVE_WEB_SOCKET` is set — the unix-socket transition TCP when `HIVE_WEB_SOCKET` is set — the unix-socket transition