Add mosaic launch <seat> with seat registration for the control board (#1504)

New package packages/seat and wrapper scripts/mosaic. `launch <seat>` writes
<dataRoot>/seats/<layout>/<seat>/registration.json and then execs the seat's
launch.sh unchanged; `seat task <seat> <text>` edits the task only. The board
reads registrations, matches by sessions directory, and lets a registered
task, project or workspace override the derived value with a source tag.
The four repository launch scripts register themselves unless already
registered or run with --check. Fleet launchers untouched; one-liner on the
plan page.

Review found the record path keyed by seat name alone (repo and fleet
"darkwing" would collide); fixed by keying on layout. Also: the Pi pin
refusal now names installed and required versions.

Tests: seat 15, control-board 89, launch scripts 5, registry 69, config 24.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
2026-09-12 10:56:17 -05:00
co-authored by Claude Fable 5.1
parent 01d9a19612
commit 69f99323c7
25 changed files with 1766 additions and 46 deletions
+20 -3
View File
@@ -82,7 +82,7 @@ node --test packages/control-board/tests/
```
<dataRoot>/board/
index.json # summary: counts, waiting-on-you list, seen list, all records
index.json # summary: counts, waiting-on-you, seen, registered, registrationErrors, all records
seen.json # Jason's "seen" marks (see below)
sessions/
<project>/
@@ -95,8 +95,9 @@ only when Jason clicks "Seen" or "Unsee" on the page (via `POST
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.
Every row also shows three "what and where" fields, each derived from the
session log and tmux or shown as the word "unknown", never guessed:
Every row also shows three "what and where" fields, each taken from the
seat's registration when it has one, else derived from the session log and
tmux, else shown as the word "unknown", never guessed:
- **Task** — the session's first user message (collapsed to one line, 240
characters). pi logs carry no task envelope, so this is the only
@@ -112,6 +113,20 @@ session log and tmux or shown as the word "unknown", never guessed:
tmux could not be asked, the `cwd` from the session log. The record says
which one it used (`workspaceSource`: `tmux-pane` or `session-cwd`).
A seat started through `scripts/mosaic launch <seat>` (see
`packages/seat/README.md`, #1504) has a registration at
`<dataRoot>/seats/<layout>/<seat>/registration.json`. The scan reads every one of
those and matches a record to a row by its sessions directory, never by
name alone. A registered task, project or workspace replaces the derived
value and the row's source field (`taskSource`, `activeProjectSource`,
`workspaceSource`) reads `registration`; the page shows a small source tag
next to the value and a "Registered" line in the detail with the start time,
harness, pid and tmux session. An empty task or a null project or workspace
in the record leaves the derived value in place. Rows with no registration
are exactly as before. The board only reads `seats/`; `mosaic launch` and
`mosaic seat task` are the only writers. A malformed record is listed in
`registrationErrors` on the index (and on stderr for `scan`) and skipped.
Marked rows are listed under a collapsed "Seen (N)" section on the page,
each with an "Unsee" button, so nothing marked is ever out of reach. Each
project table has "Hide offline" and "Hide seen" checkboxes (both on by
@@ -140,6 +155,8 @@ values), the scan refuses rather than silently dropping every mark.
"workspace": "/mnt/storage/src/mosaic-stack",
"workspaceSource": "tmux-pane",
"activeProject": "mosaic-stack",
"activeProjectSource": "workspace-git-root",
"registered": null,
"lastActivity": "2026-09-12T15:04:33.000Z",
"ageSeconds": 42,
"lastAssistantText": "Ready for the next step whenever you are.",