Check pi liveness per tmux pane and add "Seen" marks to the control board (#1503)
First step-3 refinement from Jason's daily use. Liveness now lists the panes of the agent's tmux session and counts it alive only if a pane runs pi, so killed pi sessions whose tmux session still exists show offline instead of waiting. A "Seen" button on waiting and error rows stores the row's lastActivity in <dataRoot>/board/seen.json (clicks only, never rewritten by a scan, fail closed if corrupt) and drops the row from "Waiting on you" until the agent writes anything newer; "Unsee" reverses it. New POST /api/seen route: JSON only, 4 KB limit, 400 on bad input. Tests: control-board 63/63 (30 new), registry 69/69. Review APPROVED; receipt docs/plans/reviews/2026-09-12_control-board-step3-seen-marks.md. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
@@ -14,10 +14,15 @@ rewritable; they are not run records and are not evidence.
|
||||
| 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. |
|
||||
| offline | There is no live tmux session for this agent right now, or its session exists but no longer runs `pi`. |
|
||||
| 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. |
|
||||
|
||||
Liveness means a pane in the agent's tmux session is actually running `pi`
|
||||
(`tmux list-panes -s -t '=<session>' -F '#{pane_current_command}'`). A tmux session that still
|
||||
exists but only runs bash or some other program counts as offline, not
|
||||
waiting.
|
||||
|
||||
## Commands
|
||||
|
||||
```
|
||||
@@ -42,13 +47,24 @@ bind to anything but a loopback address.
|
||||
- `--liveness tmux|assume-alive` — how to decide if an agent is alive.
|
||||
`tmux` (default) checks the real tmux session. `assume-alive` treats
|
||||
every agent as alive, useful for tests or environments without tmux.
|
||||
- `--print` — (`scan`) also print a one-line-per-agent table to stdout.
|
||||
- `--print` — (`scan`) also print a one-line-per-agent table to stdout. The
|
||||
first column is `*` for a row waiting on you, `s` for a seen row, or blank
|
||||
otherwise. The final summary line reads
|
||||
`board: <dir> (N sessions, N waiting on you, N seen)`.
|
||||
- `--port N` — (`serve`) port to listen on. Default `7331`; `0` picks a free port.
|
||||
- `--host ADDR` — (`serve`) loopback address to bind. Default `127.0.0.1`.
|
||||
Any non-loopback address is refused.
|
||||
|
||||
Routes served: `/` (the page), `/api/board` (rescan, returns `index.json`),
|
||||
`/healthz`.
|
||||
Routes served:
|
||||
|
||||
- `GET /` — the page.
|
||||
- `GET /api/board` — rescan, returns `index.json`.
|
||||
- `POST /api/seen` — mark or unmark a row as seen, then rescan and return
|
||||
`index.json`. Body must be JSON (`Content-Type: application/json`, no
|
||||
more than 4096 bytes): `{"project", "agent", "lastActivity", "seen"}`.
|
||||
`seen` defaults to `true`; pass `false` to unsee. Bad input, a missing
|
||||
header, or an oversized body gets a `400`.
|
||||
- `GET /healthz`.
|
||||
|
||||
## Exit codes
|
||||
|
||||
@@ -66,12 +82,22 @@ node --test packages/control-board/tests/
|
||||
|
||||
```
|
||||
<dataRoot>/board/
|
||||
index.json # summary: counts, waiting-on-you list, all records
|
||||
index.json # summary: counts, waiting-on-you list, seen list, all records
|
||||
seen.json # Jason's "seen" marks (see below)
|
||||
sessions/
|
||||
<project>/
|
||||
<agent>.json # one status record per agent
|
||||
```
|
||||
|
||||
`seen.json` holds `{ "<project>/<agent>": "<lastActivity>" }`. It is written
|
||||
only when Jason clicks "Seen" or "Unsee" on the page (via `POST
|
||||
/api/seen`); a scan reads it but never writes it. A mark applies only while
|
||||
the agent's newest message still has that exact `lastActivity` timestamp —
|
||||
as soon as the agent writes anything new, `lastActivity` changes, the mark
|
||||
no longer matches, and the row falls back into "Waiting on you" on its own.
|
||||
If `seen.json` exists but is not valid JSON (or not an object of string
|
||||
values), the scan refuses rather than silently dropping every mark.
|
||||
|
||||
## Example status record
|
||||
|
||||
```json
|
||||
@@ -80,6 +106,7 @@ node --test packages/control-board/tests/
|
||||
"project": "mosaic-stack",
|
||||
"state": "waiting",
|
||||
"waitingOnYou": true,
|
||||
"seen": false,
|
||||
"alive": true,
|
||||
"tmux": { "socket": null, "session": "darkwing" },
|
||||
"sessionFile": "/mnt/storage/src/mosaic-stack/.pi/state/darkwing/sessions/2026-09-12.jsonl",
|
||||
|
||||
Reference in New Issue
Block a user