Add control board web page and local server (#1503)
Step 2 of the control board MVP (MOSAIC-STACK-D-001): `serve` command starts a loopback-only local server that serves one self-contained page and re-runs the status scanner on each /api/board request. The page lists sessions waiting on Jason first (errors on top), then one table per project with plain-word states, ages, last messages, expandable detail rows, per-project hide-offline, and a 10-second auto-refresh with pause. Tests: control-board 33/33 (10 new: loopback rules, host refusal, all routes, per-request rescan, 500 path, CLI refusals, live serve, page escaping guard); registry 69/69 unchanged. Receipt: docs/plans/reviews/2026-09-12_control-board-step2-review.md. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# Control board MVP step 2: the page, implementation and review receipt
|
||||
|
||||
Date: 2026-09-12. Recorded by jarvis (Claude Code, coordinator). Tracking: #1503.
|
||||
Plan: `docs/plans/2026-09-12_control-board-mvp.md`. Step 1 receipt:
|
||||
`2026-09-12_control-board-step1-review.md`.
|
||||
|
||||
## What was built
|
||||
|
||||
- `packages/control-board/src/serve.mjs`: a small local web server with no
|
||||
dependencies and no auth. Routes: `/` (the page), `/api/board` (re-runs the
|
||||
scanner and returns `index.json`), `/healthz`, `/favicon.ico` (empty). It
|
||||
refuses to bind to anything but a loopback address.
|
||||
- `packages/control-board/src/page.html`: one self-contained page. "Waiting on
|
||||
you" at the top (error rows first), then one table per project with a
|
||||
plain-word state badge, age, and last message. Rows expand to a detail panel;
|
||||
open panels and the per-project "hide offline" choice survive the 10-second
|
||||
auto-refresh. Pause/Resume and a manual Refresh button. All API text is
|
||||
escaped before it reaches the page.
|
||||
- `src/cli.mjs`: new `serve` command (`--port`, `--host`).
|
||||
|
||||
## Tests
|
||||
|
||||
`node --test packages/control-board/tests/` — 33 tests, 33 pass, 0 fail
|
||||
(23 scanner tests from step 1, 10 new: loopback rules, host refusal without a
|
||||
socket, every route, per-request rescan, 500 path, CLI refusal/exit codes, a
|
||||
live spawned `serve`, and a static check that the page's esc() helper escapes
|
||||
every HTML-significant character and that no API value is joined into markup
|
||||
without it).
|
||||
|
||||
## Live check in a real browser
|
||||
|
||||
Server started with the full fleet and real tmux liveness on port 7332; page
|
||||
opened in Chrome through DevTools. Seen: 18 rows under "Waiting on you" with
|
||||
the three fleet 429-error rows first; two project groups (mosaic-stack 4,
|
||||
fleet 38) with "24 offline hidden"; unticking "Hide offline" showed the
|
||||
offline rows; Pause changed the header to "auto-refresh paused"; a row toggle
|
||||
opened its detail panel and stayed open across the next refresh. Console:
|
||||
only a favicon 404 (a `/favicon.ico` route was added after; no page errors).
|
||||
Screenshot: `/tmp/control-board-review.png` (not committed).
|
||||
|
||||
## Independent review
|
||||
|
||||
Reviewer: in-session code-review agent (Claude Sonnet), static read-only.
|
||||
Findings: none at reportable confidence for XSS, host-check bypass, request
|
||||
handling, fail-closed behaviour, or doc drift. It could not run the browser
|
||||
check itself (no shell or browser tools in its session), so it withheld a
|
||||
final approval on that ground; the browser check above was done by the
|
||||
coordinator instead. Two minor notes acted on: a stale test comment, and
|
||||
adding a test for the page's escaping. A separate test-writing helper flagged
|
||||
that `startServer` threw synchronously for a bad host while other failures
|
||||
rejected; it is now `async` so every failure is a rejection.
|
||||
|
||||
Corrections during this step: open detail panels originally closed on every
|
||||
refresh (fixed, keyed by section/project/agent); the escaping guard test
|
||||
initially flagged my own key-building line and a comment (reworded).
|
||||
|
||||
## Boundaries kept
|
||||
|
||||
No launcher changes, no new root files, no secrets, no auth (loopback only),
|
||||
no daemon, no changes to `packages/mosaic`. Next: step 3, daily use by Jason.
|
||||
Reference in New Issue
Block a user