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:
@@ -83,12 +83,20 @@ Board files are derived. They can be deleted and rebuilt at any time by
|
||||
running the scanner again. They are NOT run records and they are not
|
||||
evidence under the repository's write-once rules.
|
||||
|
||||
## Command
|
||||
## Commands
|
||||
|
||||
Scan once and print the table:
|
||||
|
||||
```
|
||||
node packages/control-board/src/cli.mjs scan --print
|
||||
```
|
||||
|
||||
Start the page (step 2), then open http://127.0.0.1:7331/ in a browser:
|
||||
|
||||
```
|
||||
node packages/control-board/src/cli.mjs serve
|
||||
```
|
||||
|
||||
Tests:
|
||||
|
||||
```
|
||||
|
||||
+13
-13
@@ -10,20 +10,20 @@ update this file to the next action). No ambiguity, no re-planning.
|
||||
Jason decided on 2026-09-12 (MOSAIC-STACK-D-001) that the MVP is the control
|
||||
board: one web page listing running agent sessions across projects, showing each
|
||||
one's status, and flagging which ones are waiting on him. Plan page:
|
||||
`docs/plans/2026-09-12_control-board-mvp.md`. Tracking: #1503. Step 1 (status
|
||||
scanner `packages/control-board`, plan page, ticket) is done in this checkout;
|
||||
running `node packages/control-board/src/cli.mjs scan --print` writes one status
|
||||
file per agent under `<dataRoot>/board/`. The registry line (increment 3,
|
||||
headless identity-env leak) stays parked; #1500 is closed.
|
||||
`docs/plans/2026-09-12_control-board-mvp.md`. Tracking: #1503. Steps 1 and 2
|
||||
are done in this checkout: `packages/control-board` scans every Pi agent
|
||||
(repo `.pi/state/*` and `~/.mosaic/fleet`) and serves the page.
|
||||
|
||||
Next action: step 2 of #1503 — the web page. One local page that reads
|
||||
`<dataRoot>/board/index.json`, groups sessions by project, shows the plain-word
|
||||
state per agent, puts a "waiting on you" section at the top, and refreshes
|
||||
itself (re-running the scanner on each refresh is acceptable). Reuse Dewey's
|
||||
control-board mockups under `agents/dewey/work/wui/` for layout where they fit.
|
||||
Acceptance: Jason opens the page in a browser and sees the same rows the
|
||||
`--print` output shows, with no manual steps beyond starting the page. No auth,
|
||||
no hooks/plugins, no comms, no launcher changes, no new root files.
|
||||
Start it with `node packages/control-board/src/cli.mjs serve` and open
|
||||
http://127.0.0.1:7331/ (loopback only, no auth, no daemon; Ctrl-C stops it).
|
||||
|
||||
Next action: step 3 of #1503 — daily use. Jason opens the page while he works
|
||||
and reports what is wrong, missing or confusing; each report becomes a small
|
||||
fix on this package with a test, committed and pushed to `refactor`. Close
|
||||
#1503 when Jason says the page answers "who is waiting on me" without him
|
||||
opening a terminal. Out of scope until he asks: auth, hooks/plugins, comms,
|
||||
memory, launcher changes, non-Pi harnesses, new root files. The registry line
|
||||
(increment 3, headless identity-env leak) stays parked; #1500 is closed.
|
||||
|
||||
## Completed checkpoint: #1500 increment 2 (historical)
|
||||
|
||||
|
||||
@@ -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