From a62ca1904f2fb5ec6d9780e451bb1b99deb49bcd Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sat, 12 Sep 2026 11:17:28 -0500 Subject: [PATCH] control board: brief for piece 2, reply-from-board, with Gate C (#1503) Co-Authored-By: Claude Fable 5.1 --- docs/plans/2026-09-12_control-board-mvp.md | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/docs/plans/2026-09-12_control-board-mvp.md b/docs/plans/2026-09-12_control-board-mvp.md index aa4f63fc..06ea8e38 100644 --- a/docs/plans/2026-09-12_control-board-mvp.md +++ b/docs/plans/2026-09-12_control-board-mvp.md @@ -97,6 +97,63 @@ because the launch gave no `--task`; the next real use is task instead. Fleet seats stay on the old launchers; only `agents/` seats register. Next piece in Jason's order: reply-from-board. +## Piece 2: reply-from-board + +Approved by Jason 2026-09-12 after Gate B ("seat task update works. I want to +keep things building"). Same shape as #1503/#1504: one piece, one yes/no gate, +small commits with tests on `refactor`. Darkwing opens the issue and updates +`CURRENT.md` when he picks it up. + +### What it is + +A reply box on the board. Jason types a message on a seat's row and the board +delivers it to that seat's tmux pane through the repository's own channel. +No new transport, no message store, no WebSocket. + +- **Page**: a one-line text box and a Send button in each row's detail + (registered rows only, see below). After Send the row shows one of two + receipts: `delivered` with the time, or `failed` with the tool's stderr. + Nothing else changes on the page; the next scan shows the effect (the + seat's newest user message is the text just sent, and a `waiting` row + turns `working`). +- **Server**: `POST /api/reply` with `{ "agent": "", "text": "..." }`. + The handler resolves the row's tmux session, runs + `tools/tmux/agent-send.sh -s -S ":control-board" -m ` + and returns the exit code, stdout and stderr. The exit code is the receipt; + a non-zero code is a failure the page shows verbatim. `-S` labels the + sender so the seat's preamble reads `[:control-board -> …]`. Fleet + sockets pass `-L` from the registration's `tmux.socket` when set. +- **Which rows**: only rows whose registration (#1504) names a tmux session, + and whose registration is not stale. Everything else shows "reply needs a + registered seat" instead of the box. Seat name alone is not an address; the + registration's session is. +- **Never**: raw `tmux send-keys`, a queue, retries, a history panel, a + broadcast, sending to more than one seat per request. The durable record of + what was said is the seat's own session log, which the scanner already reads. + +### Gate C (yes/no) + +Jason answers a seat that is `waiting` from the board, without opening a +terminal, and within one scan the row is `working` and its Task or last +message reflects the reply. One real exchange is the pass. + +### Tests + +- Server: a fake `agent-send.sh` on PATH captures its argv; assert `-s`, `-S`, + `-L` (when the registration has a socket) and the message text. +- Refusals with exit codes surfaced: unknown row, empty text, row without + registration, stale registration, tool exit non-zero (stderr returned). +- Page: the box appears only on registered live rows; a failure receipt shows + the stderr text. +- Loopback only stays as is; no auth is added for this piece. + +### Boundaries + +- No change to `packages/seat` or the registration record. +- No change to `tools/tmux/agent-send.sh`; if it refuses, the refusal is the + finding (ms-communications rule), reported on the page and in the issue. +- Fleet seats are out: they are not registered (Jason, 2026-09-12). + ## How the scanner decides state The scanner reads the newest pi session log for each agent, plus whether