Each project table now has "Hide seen" beside "Hide offline", both on by default, with a note saying how many rows each one hides. The choice survives the 10-second refresh. Static test pins the markup, the filter, the persistence guard, and the change handler. Sonnet review: APPROVED. Co-Authored-By: Claude Fable 5.1 <[email protected]>
147 lines
5.8 KiB
Markdown
147 lines
5.8 KiB
Markdown
# 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.
|
|
|
|
### Step 3 log
|
|
|
|
**2026-09-12 — liveness follows the tmux pane, not just the session.**
|
|
Jason found agents marked "waiting" that were actually dead: their tmux
|
|
session still existed but no longer ran `pi` (it had exited to a shell or
|
|
something else). The scanner now checks which program each tmux pane is
|
|
running (`tmux list-panes -s -t '=<session>' -F '#{pane_current_command}'`) and only counts an
|
|
agent alive if a pane is running `pi`. A tmux session with no `pi` pane is
|
|
now "offline" instead of "waiting".
|
|
|
|
**2026-09-12 — a "Seen" mark for rows that don't need a reply.** Jason
|
|
noticed most "waiting" rows were completion reports, not real asks, and
|
|
they kept cluttering "Waiting on you". The page now has a "Seen" button on
|
|
waiting/error rows; clicking it stores the row's `lastActivity` in
|
|
`<dataRoot>/board/seen.json` (via `POST /api/seen`) and drops the row out
|
|
of "Waiting on you" while keeping its state visible. The mark clears
|
|
itself the moment the agent writes anything new, and "Unsee" puts a row
|
|
back by hand. `seen.json` is only ever written by that click, never by a
|
|
scan.
|
|
|
|
## 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.
|
|
|
|
## 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:
|
|
|
|
```
|
|
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.
|
|
|
|
**2026-09-12 — a place to find what you marked Seen.** After using the
|
|
button, Jason noted the board had no way to show seen rows again. The page
|
|
now has a collapsed "Seen (N)" section between "Waiting on you" and "By
|
|
project" that lists every marked row with an Unsee button. It stays open or
|
|
closed across refreshes.
|
|
|
|
**2026-09-12 — "Hide seen" per project.** Jason suggested a checkbox like
|
|
"Hide offline" so seen rows stop cluttering the fleet table now that the
|
|
Seen section exists. Each project header has both boxes, on by default,
|
|
and the note under the table reads "N offline hidden · N seen hidden".
|