Files
stack/docs/plans/reviews/2026-09-12_control-board-step2-review.md
T
jason.woltjeandClaude Fable 5.1 ebedd1281e 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]>
2026-09-12 07:58:38 -05:00

3.1 KiB

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.