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:
2026-09-12 08:24:49 -05:00
co-authored by Claude Fable 5.1
parent ebedd1281e
commit 88d21defde
12 changed files with 777 additions and 21 deletions
+1
View File
@@ -214,3 +214,4 @@ are never rewritten or removed; corrections are new entries.
- 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.
- 2026-09-12 — dewey (Claude Code; takeover of pi session 01a08111) — WUI Phase 2 D01-D04 done, D05 delivered: five dashboard mockups (Rail, Deck, Console, Board, Stream) plus comparison at agents/dewey/work/wui/mockups/index.html; verifier 31 checks, 0 failed, 2 not verified (forced colours, screen reader); records in VERIFY.md, README.md, TASKS.md, HISTORY.md, DECISIONS.md. Stopped at Jason's design-selection gate. No v1 inspection, backend, credentials, commit or push.
- 2026-09-12 — jarvis (Claude Code, coordinator) — Control board MVP step 2 (#1503, D-001): `packages/control-board` gains `serve` (loopback-only local server, `/api/board` re-runs the scanner) and a single-file page (waiting-on-you first, per-project groups, hide-offline, expandable rows, 10s auto-refresh with pause). Suites: control-board 33/33, registry 69/69. Static review (sonnet) found no defects; live browser check by coordinator. Receipt docs/plans/reviews/2026-09-12_control-board-step2-review.md. CURRENT next action: step 3, daily use by Jason.
- 2026-09-12 — jarvis (Claude Code, coordinator) — Control board step 3, first refinement (#1503): Jason reported killed pi sessions still "waiting" and most waiting rows being completion reports. Liveness now requires a tmux pane running pi; "Seen" marks (seen.json, POST /api/seen, Seen/Unsee on the page) drop read rows out of "Waiting on you" until the agent writes again. Tests 63/63 (30 new by sonnet helper), registry 69/69; sonnet review APPROVED (README command fixed). Receipt docs/plans/reviews/2026-09-12_control-board-step3-seen-marks.md.
@@ -48,6 +48,26 @@ Acceptance:
- 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
+5
View File
@@ -17,6 +17,11 @@ are done in this checkout: `packages/control-board` scans every Pi agent
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).
First step-3 refinement landed 2026-09-12: liveness follows the tmux pane
(killed pi sessions show offline) and a "Seen" button drops read rows out of
"Waiting on you" until the agent writes again
(`docs/plans/reviews/2026-09-12_control-board-step3-seen-marks.md`).
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
@@ -0,0 +1,69 @@
# Control board step 3, first refinement: real liveness and "Seen" marks
Date: 2026-09-12. Recorded by jarvis (Claude Code, coordinator). Tracking: #1503.
Plan: `docs/plans/2026-09-12_control-board-mvp.md` (Step 3 log).
## What Jason reported
After using the page: "most of the sessions are actually just in a complete
state rather than waiting on me". Also, he had terminated the darkwing and
dewey pi sessions, and both still showed "waiting".
## Cause
1. Liveness only asked tmux whether a session by that name existed. Both
tmux sessions still existed (now running bash and Claude), so the scanner
read the old pi session log as live status.
2. Pi records that the agent finished a reply, not whether that reply was a
question or a completion report. Both landed in "waiting".
## What was built
- `tmuxIsAlive` now lists the panes of the named tmux session and reports
alive only if one runs `pi`. A session that exists but runs something else
is offline. The tmux check is injectable for tests.
- "Seen" marks: `<dataRoot>/board/seen.json` maps `project/agent` to the
`lastActivity` timestamp Jason marked. A marked row keeps its state but
leaves "Waiting on you" until the agent writes anything newer, which
changes the timestamp and clears the mark by itself. "Unsee" clears it by
hand. The file is written only by clicks (atomic, mode 0600); a scan reads
it and never rewrites it; a corrupt file refuses the scan (fail closed).
- Server: `POST /api/seen` with a JSON body; requires
`Content-Type: application/json` (so a page from another site cannot post
it without a CORS preflight, which this server does not answer); body limit
4096 bytes; 400 on bad input; 500 if the rescan fails. Other methods 405.
- Page: Seen / Unsee button beside the state badge, "seen" tag, footer count.
CLI `scan --print` shows `s` for seen rows and a seen count.
## Tests
`node --test packages/control-board/tests/`: 63 pass, 0 fail (30 new, written
by a Claude Sonnet helper against the spec; no src change was needed).
`node --test packages/mosaic/tests/`: 69 pass, 0 fail, unchanged.
## Live check
Real tmux, full fleet, Chrome via DevTools on port 7332. Before: 18 rows
waiting on Jason including darkwing and dewey. After the liveness fix: 16,
both now offline (their tmux sessions run bash). Clicking Seen on zane wrote
`{"fleet/zane": "<timestamp>"}` (mode 600), the row left "Waiting on you"
and showed a seen tag with Unsee in its project table; the API reported
15 waiting and 1 seen. Unsee emptied the file and restored 16. Console: no
messages. Screenshot `/tmp/control-board-seen.png` (not committed). The
board was left with no marks.
## Independent review
Reviewer: code-review agent (Claude Sonnet), read-only. It checked the pi
runtime source to confirm a live pi pane reports `pi` as its command (pi sets
its process title, and tool subprocesses do not take over the pane), traced
the CORS argument for the POST route, the escaping in the new markup, the
single-response paths in the server, and the stale-mark fail-safe. Verdict:
APPROVED with one finding: the README quoted the tmux command without the
`-s -t '=<session>'` flags the code uses. Fixed in README and plan page.
## Boundaries kept
Loopback only, no auth, no daemon, no launcher changes, no new root files,
no run records touched. `seen.json` is Jason's input, not derived data, and
is the only board file a scan does not regenerate.