One cumulative control-board, webui and seat state. The four rows edit the
same files (scan.mjs, page.html, README.md, app.js), so they land together,
each on its own receipt:
- Row 18, Discord connector rows on the board (#1509): R3 approved by
Darkwing and Dewey, Gitea comment 26257, manifest 254403b8. Jason
accepted the visual test.
- Row 22, board attention status (#1503): Filbert approved R1, comment
26248, manifest e40b58ec; restart receipt 26249.
- #1511, task attribution (row 6 code phase): R2 approved by Filbert and
Dewey, manifest d4c96395. docs/TOOLS.md carries the approved --by usage
line (tools-usage.patch 86bcba3c).
- #1512, relaunch activity (row 6 pilot): R1 approved by Darkwing and
Dewey, candidate manifest 47769fad. All seven source files match it.
Row 16, internal development bootstrap (#1510): the seven files outside
shared records match Filbert's R1 pins, receipt 26204 (agents/researcher/*,
scripts/test-darkwing-launch.mjs, the bootstrap plan).
packages/webui/src/public/app.js is committed at its #1512 R1 pin ce7d79a4.
The working copy holds Dewey's unreviewed return-flow candidate on top of
that, and it stays uncommitted.
Also: the four row briefs and Darkwing's evidence records under
agents/darkwing/work, including the 2026-09-26 tree manifest and the #1512
re-run against 21e3e908. Serial acceptance command: 397/397, three runs.
The failures that only show when tests run concurrently are in #1509 engine
tests, and they reproduce on clean HEAD.
Suites on the exact staged tree: config 24, task 90, foundation 43,
conductor 17, release 14, auth 15, discord 63; package union 397/397
(serial); test-darkwing-launch 5/5.
Shared records (BUILD-LOG, QUEUE, CURRENT, DEFERRED, SESSIONS, AGENTS.md,
agents/README.md) follow in Sage's records commit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
171 lines
8.6 KiB
Markdown
171 lines
8.6 KiB
Markdown
# seat
|
|
|
|
`mosaic launch <seat>` starts a seat through its existing launch script,
|
|
unchanged, and leaves one registration record that the control board reads
|
|
instead of guessing. `mosaic seat task <seat> <text>` changes the task on
|
|
that record and notes who set it. Nothing else. No provider or auth registry, no roster schema
|
|
change, no stopping or killing of seats, one record per seat.
|
|
|
|
Issue #1504. Plain ESM, no dependencies, Node 24 or newer.
|
|
|
|
## Commands
|
|
|
|
The repository wrapper is `scripts/mosaic`. It is not on PATH and is not
|
|
the npm-global `mosaic` CLI from the estate tooling, which has no `launch`
|
|
or `seat` command; run it by path.
|
|
|
|
```
|
|
scripts/mosaic launch <seat|seat-dir> [--task TEXT] [--project NAME] [--workspace PATH]
|
|
[--harness NAME] [--repo PATH] [--config PATH] [-- args...]
|
|
scripts/mosaic seat task <seat> <text> [--by NAME] [--layout repo|fleet|unknown] [--config PATH]
|
|
```
|
|
|
|
- `<seat>` is a name under `<repo>/agents/` (`--repo` defaults to the
|
|
current directory) or a path to a seat directory such as
|
|
`~/.mosaic/fleet/agents/orch-01`. Either way the directory must hold an
|
|
executable `launch.sh`.
|
|
- Everything after `--` goes to the launch script untouched.
|
|
- `launch` writes the registration, then replaces itself with the launch
|
|
script (`process.execve`). The seat keeps the same pid, terminal and
|
|
process group as if you had run `launch.sh` yourself, the launch script's
|
|
exit code is yours, and the tmux pane's foreground command stays the
|
|
harness, which the control board's liveness check depends on. Node marks
|
|
`process.execve` experimental (present since 24); it is the only way to
|
|
keep the seat's pid, so the package accepts that and pins Node 24 or
|
|
newer. The seat tests exercise it directly (exit passthrough).
|
|
- The launch script receives `MOSAIC_LAUNCH_REGISTERED=<record path>`. A
|
|
launch script that sees this variable is already registered and must not
|
|
call `mosaic launch` again; `mosaic launch` refuses to run when it is set.
|
|
- `seat task` records who set the task in `taskSetBy` (#1511): `--by NAME`
|
|
if given, else `$MOSAIC_AGENT_NAME` if set and non-empty, else the word
|
|
`unknown`. An explicit value must be one token of 1 to 64 characters
|
|
(letters, digits, `.` `_` `@` `:` `-`, starting with a letter or digit);
|
|
anything else refuses with exit 4 before the record is touched. A set but
|
|
malformed `MOSAIC_AGENT_NAME` also refuses rather than being reported as
|
|
`unknown`; `--by` is the explicit way past it. The value is never echoed
|
|
back in a refusal. **This is what the caller said, not a verified
|
|
identity.** Nothing checks it, nothing grants on it; the board displays
|
|
it next to the task and does no more with it. `launch --task` does not
|
|
set the field.
|
|
|
|
The four repository seats (`agents/darkwing`, `agents/dewey`,
|
|
`agents/filbert`, `agents/rocko`) register themselves: their `launch.sh`
|
|
re-enters through `scripts/mosaic launch` unless already registered or
|
|
called with `--check`. So `agents/darkwing/launch.sh` and
|
|
`scripts/mosaic launch darkwing --task "..."` are the same path; the second
|
|
form is how you attach a task. A `--check` run never writes a record.
|
|
|
|
## The record
|
|
|
|
`<dataRoot>/seats/<layout>/<seat>/registration.json`, directory 0700, file 0600,
|
|
written atomically. `layout` is `repo`, `fleet` or `unknown` (see below);
|
|
it is part of the path because a seat name alone is not unique, and
|
|
`seat task` refuses a bare name that is registered in more than one layout
|
|
until `--layout` says which. `dataRoot` comes from `~/.config/mosaic-dev/config.json`
|
|
(or `$MOSAIC_CONFIG`); a missing or unreadable config refuses the launch.
|
|
|
|
The record is rewritten on every launch. It is written before the launch
|
|
script runs, so a launch the script itself refuses (a failed `--check`-style
|
|
precondition, a missing context file) still leaves a record with a pid that
|
|
is no longer running; the next launch replaces it, and the board treats a
|
|
record whose pid is gone as stale (shown, but the derived values win). It is a launch record,
|
|
not a run record: it is not evidence, it holds one seat's latest launch only, and it
|
|
lives outside `<dataRoot>/board/` because the board never writes here and a
|
|
scan never changes it.
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"seat": "darkwing",
|
|
"project": "mosaic-stack",
|
|
"task": "Control board: seat registration (#1504)",
|
|
"workspace": "/mnt/storage/src/mosaic-stack",
|
|
"tmux": { "socket": null, "session": "darkwing" },
|
|
"harness": "pi",
|
|
"startedAt": "2026-09-12T16:20:11.000Z",
|
|
"pid": 431734,
|
|
"sessionsDir": "/mnt/storage/src/mosaic-stack/.pi/state/darkwing/sessions",
|
|
"seatDir": "/mnt/storage/src/mosaic-stack/agents/darkwing",
|
|
"launchScript": "/mnt/storage/src/mosaic-stack/agents/darkwing/launch.sh",
|
|
"layout": "repo",
|
|
"updatedAt": null
|
|
}
|
|
```
|
|
|
|
After `scripts/mosaic seat task darkwing "Row 6 (#1511)" --by jason` the
|
|
same record also carries `"taskSetBy": "jason"` and a non-null
|
|
`updatedAt`; `startedAt` and every other field are unchanged.
|
|
|
|
Fields asked for in the brief: `seat`, `project`, `task` (empty unless
|
|
`--task` or a later `seat task`), `workspace`, `tmux` (session name and
|
|
socket, from the `TMUX` variable of the pane the launch ran in; null outside
|
|
tmux), `harness` (only what `--harness` says; the repository launch scripts
|
|
pass `pi` or `claude-code`), `startedAt`, `pid`.
|
|
|
|
Fields added, and why:
|
|
|
|
- `sessionsDir`: how the board matches a record to a row. Seat names are not
|
|
unique across layouts (there is a `darkwing` in this repository and a
|
|
`darkwing` in the fleet), so the record names the sessions directory the
|
|
board already scans, and only an exact match counts.
|
|
- `seatDir`, `launchScript`, `layout`: what was launched and how the paths
|
|
were derived, so a wrong record can be traced without re-running anything.
|
|
`layout` is `repo` (`<repo>/agents/<seat>` with `<repo>/.git`), `fleet`
|
|
(`<seatDir>/.pi` exists) or `unknown` (nothing derived, `sessionsDir` null,
|
|
the record is still written but the board cannot match it).
|
|
- `updatedAt`: set only by `seat task`, so a task change is distinguishable
|
|
from a relaunch.
|
|
- `taskSetBy`: set only by `seat task` (see above). Optional in the shape:
|
|
a record written before the field existed loads unchanged, the board shows
|
|
`unknown` for it, and nothing rewrites it until the next `seat task` or
|
|
launch. The record version stays 1; a value that is not one bounded token
|
|
makes the record unreadable (refused, not misread).
|
|
- `version`: so a later shape change can be refused rather than misread.
|
|
|
|
`project` and `workspace` are derived only for the repo layout (the
|
|
repository's basename and root). For the fleet layout they are null unless
|
|
`--project` and `--workspace` are given, because the roster has no project
|
|
field and all fleet seats run in `~/.mosaic`; the board then keeps its own
|
|
derived values for those rows.
|
|
|
|
## How the board uses it
|
|
|
|
`packages/control-board` reads every `<dataRoot>/seats/<layout>/<seat>/registration.json`
|
|
on each scan. A registered task, project or workspace wins over the derived
|
|
value, and the row's `taskSource`, `activeProjectSource` or
|
|
`workspaceSource` says `registration`. An empty task or a null project or
|
|
workspace in the record leaves the derived value in place. Rows without a
|
|
registration are unchanged. A malformed record is reported in
|
|
`registrationErrors` on the index and skipped; it never takes the board down
|
|
and it is never treated as absent silently. A record whose pid is no
|
|
longer running is stale: still shown on the Registered line, but it does
|
|
not override anything. The row's `taskSetBy` is the record's setter only
|
|
while the task shown is the registered one (`taskSource` `registration`);
|
|
otherwise it is null, so a stale or empty registration never attributes a
|
|
transcript-derived task, and a Discord connector's fixed task never borrows
|
|
a native record's setter.
|
|
|
|
The repository launch scripts are the only launchers that register.
|
|
Fleet seats under `~/.mosaic` stay on their own launchers (Jason's ruling,
|
|
2026-09-12); a fleet seat can still be registered by hand with
|
|
`scripts/mosaic launch <seat-dir>`.
|
|
|
|
## Exit codes
|
|
|
|
`launch` exits with the launch script's own code once the script runs.
|
|
Before that: 1 the launch script could not be started (the record is
|
|
removed again), 2 invalid data or configuration (missing config, no such
|
|
seat, no executable `launch.sh`, bad record on disk), 4 usage.
|
|
`seat task`: 0 ok, 1 no registration for that seat, 2 as above, 4 usage.
|
|
|
|
## Tests
|
|
|
|
```
|
|
node --test packages/seat/tests/
|
|
```
|
|
|
|
The repository launch scripts are covered end to end by
|
|
`scripts/test-darkwing-launch.mjs` (darkwing, dewey, filbert) and
|
|
`scripts/test-rocko-launch.mjs`, which run each `launch.sh` in a fixture
|
|
root that is also its own data root, so no registration reaches a real one.
|