Add control board status scanner and MVP plan (#1503)

Step 1 of the control board MVP (decision MOSAIC-STACK-D-001): a plan page,
Gitea #1503, and packages/control-board, which reads each agent's newest pi
session log plus tmux liveness and writes one status file per agent under
<dataRoot>/board/. 23/23 tests; independent review approved after three
fixes (length stopReason as error, unknown liveness state, secrets-boundary
test). CURRENT.md now points at step 2, the page.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
2026-09-12 07:26:03 -05:00
co-authored by Claude Fable 5.1
parent 1993039c76
commit b9f59a5903
10 changed files with 1003 additions and 15 deletions
+3
View File
@@ -209,3 +209,6 @@ are never rewritten or removed; corrections are new entries.
- 2026-09-12 — jarvis (Claude Code; owner-directed takeover of darkwing pi session 01a07cfa) — Recovered #1500 state from the session log; demo test reran 2/2 on 5abbabb7; issue open, remote refactor still 5abbabb7. Diagnosed owner demo failure: alacritty bash pid 2444405 (started 2026-09-07 14:26 UTC, before cutover) has cwd inode in the retired v1 copy; logical pwd printed the canonical path, HEAD read 5d277000, and a 2026-09-12 git pull fast-forwarded that copy to next 2101c9b4 (fred, #1502). No repository changes, commit or push; darkwing pi (pid 45527) left running.
- 2026-09-12 — jarvis (Claude Code, darkwing takeover) — #1500 owner acceptance recorded (Jason demo 2/2 at 5abbabb7) and issue closed via scripts/gitea-api.sh (comment 26007; Mosaic issue-close wrapper refused on seat token, fail-closed). Records-only commit; CURRENT next action is MVP re-plan against MOSAIC-STACK-D-001.
- 2026-09-12 — dewey (Claude Code; owner-directed takeover of dewey pi session 01a08111) — Resumed WUI Phase 2 from agents/dewey/work/wui/TASKS.md: D01 source-linked feature inventory, then D02-D05 five dashboard mockups and comparison page, folding in MOSAIC-STACK-D-001 control board as the landing view. Prototype files only under agents/dewey/work/wui; no v1 inspection, backend, credentials, commit or push.
- 2026-09-12 — jarvis (Claude Code, coordinator) — Control board MVP step 1 (#1503, D-001): plan page, ticket, `packages/control-board` scanner + CLI, 23/23 tests, live scan 42 sessions/18 waiting on Jason; independent review CHANGES REQUIRED then APPROVED after three fixes (length→error, unknown liveness state, secrets-boundary test). Receipt docs/plans/reviews/2026-09-12_control-board-step1-review.md. CURRENT next action: step 2, the page.
+107
View File
@@ -0,0 +1,107 @@
# Control board MVP — plan
Tracking: Gitea issue #1503 (https://git.mosaicstack.dev/mosaicstack/stack/issues/1503)
## Why
Jason is building blind. He has many agent sessions running across projects
and no single place to see what they are doing or who is waiting on him.
This plan replaces the registry line as the next thing to build. Registry
increment 3 stays parked; it does not resume by inertia. Decision record:
`MOSAIC-STACK-D-001` (Jason, 2026-09-12).
## What "done" looks like for the MVP
One web page lists my running agent sessions across projects, shows each
one's current status, and flags which ones are waiting on me.
Acceptance:
- Jason opens one page and sees every live pi agent, grouped by project.
- Each agent shows a state in plain words (working, waiting, error,
offline, idle, unknown).
- A "waiting on you" section sits at the top of the page.
- The page refreshes itself. Jason does not have to reload it by hand.
- Every state on the page comes from a status file written by the scanner,
not from a guess made in the page itself.
## Three steps
### Step 1 (today): plan, ticket, scanner
- Write this plan and open the Gitea ticket.
- Build the status scanner script with tests. Running it writes one status
file per agent under `<dataRoot>/board/`.
- This step does not touch any page or launcher. It only produces files.
### Step 2: the web page
- Build a page that reads the status files the scanner writes and shows
them grouped by project, with the waiting-on-you section on top.
- The page refreshes itself on a short timer. The small local server behind
it may re-run the scanner on each refresh so the files stay fresh. No
separate daemon is needed for this step.
### Step 3: daily use and fixes
- Jason uses the page every day for real work.
- He says what is wrong or missing.
- Fix that one thing, then repeat. No new scope beyond what he actually hits.
## How the scanner decides state
The scanner reads the newest pi session log for each agent, plus whether
the agent's tmux session is still alive. It does not guess; it only reports
what the log and tmux say.
| State | Plain-words meaning |
|---------|----------------------|
| working | The agent is in the middle of a turn: thinking or running a tool. |
| waiting | The agent finished its turn. It is your move now. |
| error | The agent's last turn ended in an error, was aborted, or was cut off. Go look at it. |
| offline | There is no live tmux session for this agent right now. |
| idle | The agent is live but has not had a conversation yet. |
| unknown | The scanner could not ask tmux (missing or not answering). It does not assume the agent is alive. |
Two kinds of agents are scanned today:
- Repo agents: session logs live under `.pi/state/<agent>/sessions` in a
project checkout, on the default tmux socket.
- Fleet agents: session logs live under
`~/.mosaic/fleet/agents/<agent>/.pi/agent/sessions`, on the tmux socket
named `mosaic-fleet`.
## Where files go
- One file per agent: `<dataRoot>/board/sessions/<project>/<agent>.json`
- One summary file for the whole board: `<dataRoot>/board/index.json`
- `dataRoot` comes from `~/.config/mosaic-dev/config.json`. If that config
is missing or broken, the scanner refuses to run. It does not guess a
fallback location.
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
```
node packages/control-board/src/cli.mjs scan --print
```
Tests:
```
node --test packages/control-board/tests/
```
## Boundaries
- No changes to any launcher script.
- No new files at the repository root.
- No secrets read, stored, or printed.
- No daemon yet. In step 1 the scanner is run by hand. In step 2 the page
may trigger it on refresh; nothing runs it on a schedule.
- No changes to `packages/mosaic`.
- This plan does not authorize push or merge beyond whatever the existing
refactor-branch plan already allows.
+16 -15
View File
@@ -7,22 +7,23 @@ update this file to the next action). No ambiguity, no re-planning.
## Next action
Jason accepted the #1500 fixture-only increment on 2026-09-12 by running the
two-test demonstration himself on the canonical checkout at 5abbabb7 (2/2 pass);
receipt `docs/plans/reviews/2026-09-12_m20-increment2-owner-acceptance.md`.
#1500 is closed. No increment 3, launcher integration, production refresh or
deployment is authorized by that acceptance; the headless identity-env leak
remains a separate intake owned by Darkwing.
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.
Next action: re-plan against Jason's 2026-09-12 MVP decision MOSAIC-STACK-D-001
(control board: one page listing running agent sessions across projects with
status and a "waiting on you" section, deliberately smaller than the current
registry line). Present Jason a concrete plan that reconciles the registry
queue (increment 3 candidates, identity-env fix) with that MVP, with a
recommendation and acceptance evidence, before starting any new implementation.
Owner of this planning action: the active coordinator session (jarvis, Claude
Code, taking over darkwing's pi session). The registry queue does not resume by
inertia.
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.
## Completed checkpoint: #1500 increment 2 (historical)
@@ -0,0 +1,50 @@
# Control board MVP step 1: 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`. Decision: MOSAIC-STACK-D-001.
## What was built
`packages/control-board/` (ESM, no dependencies, node >= 24): `src/scan.mjs`
reads each agent's newest pi session log plus tmux liveness and writes one
status file per agent under `<dataRoot>/board/sessions/<project>/<agent>.json`
and a summary `index.json`. `src/cli.mjs` wraps it (`scan --print`). Config is
read fail-closed from `~/.config/mosaic-dev/config.json`. Board files are
derived and rewritable; they are not run records.
## Tests
`node --test packages/control-board/tests/` — 23 tests, 23 pass, 0 fail.
`node --test packages/mosaic/tests/` still 69/69 (untouched, checked for drift).
## Live run
`node packages/control-board/src/cli.mjs scan --print` on this host: 42 sessions
(4 repo agents, 38 fleet agents); 15 waiting, 3 error (all three are pi
usage-limit 429 errors on 2026-09-11), 24 offline, 0 working, 0 idle, 0 unknown;
18 flagged waiting on Jason. Spot checks: `arena-100` offline matched
`tmux -L mosaic-fleet has-session` refusing; the error rows matched the raw
`stopReason: "error"` entries.
## Independent review
Reviewer: in-session code-review agent (Claude Sonnet), read-only, with access to
the installed pi runtime for stopReason verification. First verdict: CHANGES
REQUIRED with three findings, all fixed in the same pass:
1. pi emits `stopReason: "length"` when a response is cut off; it fell through to
"working". Now mapped to "error".
2. A tmux check that could not run (`null`) was treated as alive. Now reported as
a distinct "unknown" state, never counted as waiting on you.
3. No test pinned the rule that the scanner only walks inside the given sessions
directory (fleet auth symlinks sit beside it). Added a sibling-secrets test.
Second verdict: APPROVED. The reviewer also noted one imprecision in my change
summary (a lastError test I described as new in the fix round had been added just
before the round); recorded here for honesty.
## Boundaries kept
No launcher changes, no new root files, no secrets read or printed, no changes to
`packages/mosaic`, no daemon, no web page yet. Next action (CURRENT.md): step 2,
the page.