docs: lead decision 94, dogfood cutover rows, Jason starts the Console (#1569, #1570)

Decision 94 records Q94 yes and Mos's sage-0054/0055 directions: no broker
row (the Console already reads as the human), Jason starts the Console,
nine rows deferred, S7's line amended, Gate E partial pass on the dogfood
day, no rollback hostname. New brief for the Console service row and the
send-to-a-role row. Row 74's start line and S7's request line amended.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-11 17:17:35 -05:00
co-authored by Claude Opus 5.5
parent b977dc2873
commit 8334a52331
4 changed files with 202 additions and 6 deletions
+45
View File
@@ -1999,3 +1999,48 @@ which stay with him. Each item names who decided it and what happened.
likely X-authentik-uid. The Console check stays generic (one header likely X-authentik-uid. The Console check stays generic (one header
name, one user id, from flags). Jason's Q93, on the Authentik token, name, one user id, from flags). Jason's Q93, on the Authentik token,
gates ops-01's application, not the Console's landing or install. gates ops-01's application, not the Console's landing or install.
94. **Dogfood on mosaic.woltje.com within 24 hours; Jason starts the
Console (2026-10-11).** Jason, relayed by Mos (sage-0053): "I want the
system online and us able to dogfood in the next 24 hours." Q94, his
yes to Sage's reading (sage-0055): "Yes, but the old build didn't work
anyway. Rollback is likely not a desirable action." Mos's directions
are in sage-0054 and sage-0055.
- The Console already reads the bus as the human. Its bus reads run
the human transport (`packages/webui/src/bus.mjs`, `humanCall`), so
the broker binds a human capability after `verifyHuman`, and only
Console code limits it to the reads in `packages/bus/src/views.mjs`.
A send from the browser needs a Console route, not new broker
authority. The broker row Sage proposed in the sage-0053 reply (row
A) is dropped. Q95, web write authority, is a ruling on that route.
- The process that starts the Console holds human authority. An agent
that starts it through `systemd-run` or a user unit gets a clean
environment and an ancestry the proof accepts: the detachment gap
decision 62 names, which T3 seats must not use. So Jason starts the
Console himself, the 03:00Z read-only fallback included. This
replaces decision 93's "Sage starts the preview"; the row 74 brief
says so (amended line, re-pinned). Agents may install and render the
unit and read its status, never start, stop or restart it.
- New rows, brief `docs/plans/2026-10-11_dogfood-cutover.md`: the
Console user service (owner Rocko, reviewer Filbert), which starts
now ahead of rows 72 and 73; and a human message to a role from the
Console (owner Dewey after row 74, reviewers Darkwing and Filbert),
blocked on Q95.
- Deferred until after cutover, each moved to blocked with that
reason: rows 49, 61, 63, 64, 65, 66, 69, 70 and 71. Rows 58, 59, 60,
67, 72 and 73 continue around the cutover reviews.
- S7 (row 42): Jason's five requests may come from the WebUI or the
CLI; the brief's one line is amended and re-pinned. S7 runs with
the cutover, and his first five requests to the PM are its pieces.
- Gate E (row 5): the dogfood day is its demonstration for the scope
that exists, the managed PM on the bus. Jason's ruling on that day
records a partial pass. The all-seat part stays open and row 5
stays waiting on Jason; it isn't called passed.
- No rollback hostname. The old build isn't a rollback target, so
ops-01 builds no rollback name or flip-back. Its data is kept until
Jason rules on deleting it. The live cutover still needs his
approval at the time.
- The tradeoff: no agent can restart the Console. After a crash the
unit's `Restart=on-failure` brings it back; after a reboot, or a
logout without linger, it waits for Jason. Deferring nine rows
pushes the skills, routing and hygiene work back by about two days.
+2 -2
View File
@@ -596,8 +596,8 @@ The PRD's acceptance can only be shown end to end.
### What ships ### What ships
Five one-sentence requests from Jason in the CLI, each carried to a Five one-sentence requests from Jason, sent from the WebUI or the CLI
closed, reviewed task. For each: the trail in `mosaic trail` and the (lead decision 94), each carried to a closed, reviewed task. For each: the trail in `mosaic trail` and the
WebUI, the decisions that reached Jason (gated only), and the event WebUI, the decisions that reached Jason (gated only), and the event
counts from the bus. Those counts are the first measures (REQ-EVT-1). counts from the bus. Those counts are the first measures (REQ-EVT-1).
+147
View File
@@ -0,0 +1,147 @@
# Dogfood cutover: the Console as a service, then sending to a role (2026-10-11)
Status: written by Sage, lead. It follows `docs/plans/BRIEF-TEMPLATE.md`.
Two rows, from Mos's sage-0054 (lead decision 94). The service row starts
now. The send row is briefed only; it starts after Jason answers Q95
(web write authority), and is amended first if his answer narrows it.
Q94, the cutover reading, is answered yes.
What the code already does, and why there is no broker row. The Console
reads the bus as the human. `packages/webui/src/bus.mjs` runs the S4
human transport (`packages/bus/src/human-cli.mjs`) as a child, so the
broker binds a human capability (`bindHuman`, `via: 'cli'`) after
`verifyHuman` checks the process and its ancestry
(`packages/bus/src/human.mjs`). The Console limits itself to the read
verbs in `packages/bus/src/views.mjs`; the broker doesn't. So a send from
the browser needs no new broker authority, only a Console route, and the
Q95 ruling is about that route. It also means the process that starts
the Console decides whether it holds human authority. A Console started
from an agent's shell, detached through systemd, passes the ancestry
check: the gap lead decision 62 names. So Jason starts it himself.
## Console as a user service
### Problem
Row 74 cut the service script to make the 10-12 date. Daily use can't run
on a transient unit, which a reboot or a logout without linger ends and
nobody reviewed as a file. The bus has `scripts/bus-service.sh`; the
Console has nothing.
### Owner and reviewer
Owner: Rocko. Reviewer: Filbert. Rocko has no Gitea token; Sage runs the
review request as jarvis.
### Files owned
- `scripts/webui-service.sh` (new)
- `packages/webui/systemd/mosaic-webui.service.in` (new)
- `packages/webui/tests/service.test.mjs` (new)
- `docs/TOOLS.md`, the new script's entry only (row 70, the other row
editing it, is deferred)
- `agents/rocko/work/queue-<row>/`
Not `packages/webui/README.md` or any `src/` file: row 74 owns them. The
README pointer to the script is a line Sage adds after both rows land.
### What ships
- `scripts/webui-service.sh render|install|uninstall|status` on the
`bus-service.sh` pattern and exit codes (0 ok, 1 failed, 4 usage):
`render` prints the unit; `install [--dir DIR] [--no-reload]` writes it
to a temp file, renames it and runs `systemctl --user daemon-reload`;
`uninstall` refuses while the unit is active; `status` prints the unit
state and the result of `GET /healthz` on its port.
- No start, stop, enable or restart verb. The script header and the
TOOLS.md entry say why: the Console holds the human's capability, so
Jason starts it from his own shell with
`systemctl --user enable --now mosaic-webui`.
- `render` and `install` take `--port N` (default 7332), `--business ID`,
`--board URL`, and row 74's `--public-host NAME --auth-header HEADER
--allow-user ID`, all three or none, as `serve` takes them. Each value
is held to a plain character set the unit can carry (systemd expands
`%` and splits on spaces) and refused otherwise. None is a secret.
- `mosaic-webui.service`: one instance, `WorkingDirectory` and `PATH`
filled in as the bus unit does, `ExecStart` running
`node @REPO@/packages/webui/src/cli.mjs serve` with the rendered flags,
`Restart=on-failure`, `RestartPreventExitStatus` for usage and config
exits, `NoNewPrivileges=yes`, `WantedBy=default.target`. It sets no
`MOSAIC_*`, `CLAUDE*`, `CODEX*` or `PI_*` variable.
- Tests: render with and without the preview flags; a partial preview
set refused; a value with a space, `%` or newline refused; the rendered
unit names none of the agent markers in `packages/bus/src/human.mjs`;
install into `--dir` with `--no-reload` writes the file whole;
uninstall refuses while active (a stand-in `systemctl` on `PATH`).
### Out of scope
Starting the unit; the board's service; any `src/` change; a container.
If the unit can't run `serve` as written, stop and tell Sage.
### Gate
Filbert approves on the row's issue naming the candidate manifest. The
webui node suite and every `scripts/test-*.sh` green on Sage's gate rerun.
## Console sends a message to a role
### Problem
For dogfooding, Jason has to ask the PM for work from the browser. Tasks
don't need their own path: the PM creates and assigns them (S3). One write
covers it, a human `message.send` to a role. The Console refuses every
POST in preview mode (row 74), and outside it has no bus write at all.
### Owner and reviewer
Owner: Dewey, after row 74. Reviewers: Darkwing and Filbert.
### Files owned
- `packages/webui/src/` (serve, bus, cli and the page assets under
`public/`), `packages/webui/README.md` and `packages/webui/tests/`
- `agents/dewey/work/queue-<row>/`
### What ships
- A `--send` flag on `serve`, off by default, accepted only with the
three preview flags. Without it the Console behaves as after row 74.
- `POST /api/bus/message` with JSON `{to, body, in_reply_to?}`, at most
16 KiB. With `--send` it is the one POST preview mode allows. It
requires the auth header equal to the allowed user on every request,
loopback included, an Origin equal to the request's own origin, and
`Content-Type: application/json`. It calls `message.send` through the
same human transport the reads use, with exactly those keys. `to` and
the body limits are the broker's to refuse; its codes map to HTTP the
way `busStatus` maps the reads'.
- `outcome-unknown` is shown as unknown, with no resend: the page says
the message may have been sent and points at the trail. Nothing retries
automatically.
- One log line per send on stdout (time, allowed user, `to`, message id
or error code), never the body.
- On the page: a role picker from the agents read, a text box and Send.
The receipt shows the message id and links its trail; replies show in
the inbox as today. A draft survives the periodic refresh.
- Tests: each refusal (no `--send`, missing or wrong header, a loopback
request without the header, bad Origin, wrong type, oversize, an extra
key), a send through a stand-in transport with exactly the allowed
keys, `outcome-unknown` reported and not retried, and every other POST
still 403 `read-only`.
- The README states what `--send` grants: anyone the proxy lets through
as the allowed user, and any local process that reaches the port and
sets the header, can send a human instruction. The second is the same
class of exposure as running the human CLI directly (lead decision 62).
### Out of scope
Decisions from the browser (seen, resolve, withdraw) stay in the CLI
(REQ-DEC-3); message classes other than the default; corrections; any
bus or broker change; conversations with T3 seats (CHAT-07).
### Gate
Darkwing and Filbert approve on the row's issue naming the candidate
manifest. The webui, bus, control-board and cli node suites and every
`scripts/test-*.sh` green on Sage's gate rerun. Jason's Q95 answer is
recorded before the row starts.
+8 -4
View File
@@ -99,10 +99,14 @@ exactly as today, and the existing tests show it.
- A container image, CI deploy, or changes to the bus unit. - A container image, CI deploy, or changes to the bus unit.
- A service script and unit template (`scripts/webui-service.sh` on the - A service script and unit template (`scripts/webui-service.sh` on the
`bus-service.sh` pattern). That is a follow-up row. `bus-service.sh` pattern). That is a follow-up row.
- Starting it on dragon-lin. After the row lands, Sage starts the preview - Starting it on dragon-lin. The Console reads the bus through the human
as a transient user unit (`systemd-run --user`, loopback port 7332; transport, so whoever starts it holds the human's capability. An agent
7330 holds the operator's own Console). The edge reaches it once starting it, even detached through systemd, works around the human
rev-sol-01 passes ops-01's route. proof (lead decisions 62 and 94). After the row lands, Jason starts it
from his own shell as a transient user unit (`systemd-run --user`,
loopback port 7332; 7330 holds the operator's own Console), with the
command Sage gives him. The edge reaches it once rev-sol-01 passes
ops-01's route.
- The group check. Authentik and the proxy limit sign-in to Jason's - The group check. Authentik and the proxy limit sign-in to Jason's
group; the Console checks the header against the one user id. group; the Console checks the header against the one user id.