The console takes the design package's tokens.css and icons.svg as byte
copies, guarded by a test, and a new shell.css holds the components:
command bar with freshness line, section list (a strip that scrolls
inside itself below 760px), dense table, inspector, status mark, chips,
copy command, toast, Ctrl+K palette, not found, banners and skeleton
rows. JetBrains Mono 2.304 ships from the official archive with sha256
and OFL. Harbor following the system setting is the default palette.
A refusal (403, not-configured, no-bus-host, or the board's 403) fails
closed everywhere: no kept rows, counts, inspector, palette entries or
conversation history, and a later 500 or 503 doesn't bring them back.
Dewey built it in three rounds. Round 1 (75569953) and round 2
(78c3aef9) got changes from Darkwing (27148, 27165) and Filbert (27143,
27163) for refusal leaks outside the main view; round 3 (2562c05d) was
approved by Darkwing (27171) and Filbert (27174). Sage's gate on
ea080fd8 plus the candidate: runs 41/0, queue 148/0, webui 27/0,
conversation 182/0, control-board 124/0, build-tokens --check current,
every scripts/test-*.sh 0 failed (task 98/0 and release 14/0 with
Docker). The round 2 gate had one discord engine timing failure in the
full run, 66/0 alone, filed as #1553.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
242 lines
14 KiB
Markdown
242 lines
14 KiB
Markdown
# Mosaic Console
|
|
|
|
Piece 4, #1507. The control board is the first and only WebUI screen.
|
|
|
|
## Run
|
|
|
|
Start the existing board in one terminal, then the WebUI in another:
|
|
|
|
```sh
|
|
node packages/control-board/src/cli.mjs serve
|
|
node packages/webui/src/cli.mjs serve
|
|
```
|
|
|
|
Open http://127.0.0.1:7330/. Optional `--port N` and
|
|
`--board http://127.0.0.1:7331` select another port or loopback board origin.
|
|
`--business ID` picks the business the bus views read; without it they read
|
|
the running bus host's business (`mosaic bus start <business>`).
|
|
Port 0 picks a free port. Ctrl-C stops each foreground server. No daemon,
|
|
installation, account, authentication or deployment is added.
|
|
|
|
Only HTTP loopback board origins are accepted. The WebUI binds to IPv4 loopback
|
|
by default, rejects nonlocal Host and cross-origin requests, sends no CORS
|
|
headers, refuses redirects and accepts only JSON object POST bodies up to 4096
|
|
bytes. Do not expose either unauthenticated server through a public proxy.
|
|
|
|
## Use
|
|
|
|
- Waiting on you uses the board's `waitingOnYou` flag across all projects.
|
|
- Select a project on the left to filter the session table. Counts show visible
|
|
rows out of the total when Hide offline or Hide seen hides anything.
|
|
- Select an agent to open its inspector. Arrow keys move between table agent
|
|
buttons, Enter opens, and Close or Escape in the inspector returns focus.
|
|
- Seen clears the row from Waiting on you until the board detects new activity.
|
|
The collapsed Seen section keeps those rows available; Unsee returns them.
|
|
- Reply appears only where the board's registration permits it. A failure shows
|
|
the board's stderr and keeps the draft. Transport success is the board's
|
|
`delivered` receipt, not proof that the seat processed the message.
|
|
- Refresh runs the existing board scan. Automatic refresh uses the board page's
|
|
ten-second interval; Pause stops it. Failed refreshes keep the last snapshot
|
|
with a warning. There are no automatic action retries.
|
|
- Palette and appearance come from `tokens.css` (see "Shell" below) and persist
|
|
in this browser when local storage is available. Appearance defaults to
|
|
System. No settings screen is added.
|
|
- History opens a read-only conversation view for a seat, from its Waiting
|
|
card, its table row or its inspector (#1507, CHAT-02). It shows the whole
|
|
branch with nothing clipped. Tool calls, tool results and thinking start
|
|
collapsed. Session text is always shown as text: Markdown stays as source,
|
|
and terminal controls, bidi controls and marks show as visible symbols. Reply in the view
|
|
uses the same board reply path as the inspector.
|
|
- The view checks for new entries on the same ten-second refresh, and Pause
|
|
stops it. It never switches on its own. A fork, a newer session for the seat
|
|
or a rewritten file each shows a marker with a button to open the other
|
|
history. Reloading after a rewrite keeps the branch the view was on; if that
|
|
branch is gone, the view opens the latest one and says so. Session lists
|
|
older sessions for the seat. Only repository Pi seats
|
|
have history; other harnesses say so.
|
|
|
|
Drafts and receipts stay in page memory, including across refresh, inspector
|
|
changes and a stale registration. Reloading or closing the page loses them.
|
|
A pending send stays disabled across refresh; new text typed during a send is
|
|
not cleared by the earlier send's success. If the proxy loses the response,
|
|
delivery may be unknown: inspect the seat before sending again.
|
|
|
|
## Inbox, tasks, agents and trails (slice 1 S5, #1522)
|
|
|
|
The Console sidebar adds Inbox, Tasks and Agents next to the control board,
|
|
which stays at `#/`. Routes: `#/inbox`, `#/inbox/<decision>`, `#/tasks`,
|
|
`#/tasks/<vikunja:project/task>`, `#/agents`, `#/trail/task/<ref>` and
|
|
`#/trail/decision/<id>`, with `?kind=` filters on a trail. The design
|
|
note is `agents/dewey/work/wui/SLICE1-VIEWS.md`.
|
|
|
|
- Every view reads the four verbs of the Q1 module (`packages/bus/src/views.mjs`,
|
|
lead decision 56): `inbox`, `tasks`, `agents` and `trail`. The CLI reads the
|
|
same functions, so both show the same broker data.
|
|
- Nothing writes. A decision shows `mosaic decide <id> <key>` for each choice,
|
|
with Copy, which only puts the command on the clipboard. It is answered in a
|
|
terminal (REQ-DEC-3, Q4). Seen is not set from the browser either. The
|
|
command has no `--business`, so `mosaic decide` uses the running bus host's
|
|
business, as the Console does by default. From a Console started with
|
|
`--business` for another business the copied command refuses (no bus host,
|
|
or no such decision) rather than answering elsewhere.
|
|
- What no Q1 verb returns is labelled where it would show, for example
|
|
"Bot names: not in the Q1 module" (lead decision 63). Nothing is guessed.
|
|
- A task's list row can only say its current state came from a Vikunja read,
|
|
since the list has no trail. The task page reads the trail and tells a task
|
|
the stack never created from one changed in Vikunja after the stack wrote it.
|
|
A stale poll read is shown in the snapshots and never as current.
|
|
- All bus- and agent-authored text is rendered inert: controls show as control
|
|
pictures or `[U+XXXX]`, and Markdown stays as source.
|
|
- Views refresh on the board's ten-second interval and stop on Pause. Focus,
|
|
open sections and the copy status survive a refresh.
|
|
- A failed read keeps the last good answer for that read and says it is old,
|
|
with the time it was read. A refusal (403, `not-configured` or `no-bus-host`)
|
|
never shows kept rows (row 53), and it drops every kept bus read: the Inbox
|
|
count and the palette's decisions and tasks go too, until a read succeeds.
|
|
A page never read, or refused, shows the failure instead,
|
|
titled "Bus refused the read" (403: the bus refused the Console's read, for
|
|
example `human-required` from a Console started inside an agent run), "No bus to read" (no system
|
|
config, or no bus host and no `--business`) or "The read failed".
|
|
|
|
## Shell (row 53, #1542)
|
|
|
|
The Console is restyled to `docs/design` (`IMPLEMENTING.md`). It is a restyle
|
|
only: no new data, route or write.
|
|
|
|
- `src/public/tokens.css` and `src/public/icons.svg` are byte copies of
|
|
`docs/design/tokens.css` and `docs/design/icons.svg`; `tests/shell.test.mjs`
|
|
fails if either differs. Edit `docs/design`, rebuild there, then copy.
|
|
- Colours come only from `tokens.css`, selected by `data-palette` and
|
|
`data-mode` on `<html>`. The page sets no colour inline. Appearance System
|
|
removes `data-mode`, so the OS light or dark setting applies; it is the
|
|
default, with Harbor. The choice is kept under `mosaic-console-appearance`.
|
|
- `shell.css` loads last and holds the restyle: radii `--r` 6px and `--r-lg`
|
|
10px, the type scale, tabular numbers, the command bar, section list, dense
|
|
table, inspector, status marks, class chips, toast, command palette and
|
|
not-found page. The earlier CSS files are unchanged.
|
|
- JetBrains Mono 400, 500 and 700 come from the official JetBrains release
|
|
(v2.304); `assets/fonts/jetbrains-mono-sources.txt` has the URL and sha256
|
|
of the archive and each file, and `jetbrains-mono-OFL.txt` the licence.
|
|
Manrope has no 800 file here, so 800 headings render at 700.
|
|
- Ctrl+K (or the search field) opens the palette over views, inbox decisions
|
|
and tasks; arrows move, Enter opens, Esc closes and returns focus. Arrow
|
|
keys also move between task links in the bus tables.
|
|
- Copy reports in a toast. If the clipboard is unavailable, the toast stays
|
|
until dismissed and the command is selected for Ctrl+C. When no toast shows,
|
|
the toast is an empty live region, visually hidden but still in the
|
|
accessibility tree, so the first copy is announced.
|
|
- The command bar shows, in UTC, when the board was scanned and the bus read,
|
|
and says when the last attempt failed. A board refusal drops the last scan
|
|
and everything drawn from it: the cards, the table, the inspector, the
|
|
project filter and tree, the counts, the footer and the scan line all say
|
|
the board was not read. A failed read with no scan held, after a refusal or
|
|
before the first scan, shows the board empty and says the read failed; it
|
|
never brings back rows from a refused scan. A refusal also closes an open
|
|
conversation and clears its history.
|
|
- From 760px the footer is a one-line status bar and the side column fits
|
|
between it and the command bar. Below 760px the section list is one strip
|
|
that scrolls inside itself.
|
|
|
|
## Data and boundaries
|
|
|
|
GET `/api/board`, `/api/conversations` and `/api/conversation`, and POST
|
|
`/api/seen` and `/api/reply` proxy only those board paths. Only the two
|
|
conversation routes carry their query string; the board validates it. POST
|
|
bytes and upstream status/JSON are preserved. GET `/api/config` returns the
|
|
configured board URL for the page's error message. No scanner, registration,
|
|
session reader or transport is implemented here. The session reader is
|
|
`packages/conversation`, served by the board.
|
|
|
|
GET `/api/bus/inbox`, `/api/bus/tasks`, `/api/bus/agents` and
|
|
`/api/bus/trail?subject=<id>` are the bus reads (`src/bus.mjs`). Any other
|
|
method is 405. The subject must match the broker's identifier rule, else 400.
|
|
Each read runs the S4 human transport (`packages/bus/src/human-cli.mjs`) as
|
|
a child process, so a slow read doesn't stall the server. The bus checks
|
|
that child's ancestry, which ends at the Console process; it never sees the
|
|
browser or any other client of the port. So while the Console runs,
|
|
anything that can connect to its port (7330 by default) reads what a reader
|
|
capability reads ("Human and reader paths" in `packages/bus/README.md`):
|
|
the inbox, tasks, agents and trails. That includes a T3 seat using `curl`
|
|
and a managed S6 session, since S6 doesn't confine the network. No route
|
|
writes (Q4), so the exposure is reads only. Slice 1 doesn't change this
|
|
(Filbert F1 on #1522). Answers are `{ rows, at }`; failures are `{ error, message }`
|
|
with 403 for a refusal, 503 for no bus or no answer, and 502 for an answer
|
|
that can't be used. The bus needs the system config
|
|
(`~/.config/mosaic-dev/config.json`); without it the board still serves and
|
|
the bus routes answer `not-configured`.
|
|
|
|
Console's shared CSS, Console CSS, brand.js and local Manrope fonts were copied
|
|
unchanged from `agents/dewey/work/wui/`. Font license and source URLs accompany
|
|
the files under `src/public/assets/fonts/`. `live.css` contains the live-page
|
|
adaptations; original mockups and unrelated pending design work stay untouched.
|
|
Unsupported mockup fields and controls are listed in `docs/plans/DEFERRED.md`.
|
|
The source tree is a project filter; no invented workspace registration tree
|
|
or mockup session hierarchy is presented as live data.
|
|
|
|
## Verify
|
|
|
|
```sh
|
|
node --test packages/webui/tests/
|
|
node --test packages/control-board/tests/ packages/seat/tests/ packages/ledger/tests/ packages/mosaic/tests/
|
|
WEBUI_EVIDENCE=/tmp/webui-evidence node --test packages/webui/tests/browser.test.mjs
|
|
WEBUI_EVIDENCE=/tmp/webui-evidence node --test packages/webui/tests/conversation.test.mjs
|
|
WEBUI_EVIDENCE=/tmp/webui-evidence node --test packages/webui/tests/bus-browser.test.mjs
|
|
WEBUI_EVIDENCE=/tmp/webui-evidence node --test packages/webui/tests/shell.test.mjs
|
|
```
|
|
|
|
Node's test runner and installed `/usr/bin/chromium` are required. Set `CHROMIUM`
|
|
to another installed Chromium path. No npm download is needed. The copied CDP
|
|
helper starts a separate temporary browser profile and removes it on exit.
|
|
Tests use temporary board session/registration files and stub the board's
|
|
transport. They never send to real seats or read real session logs.
|
|
|
|
Browser tests exercise the rendered real board fixture, exact counts, project
|
|
filtering, keyboard return focus, Seen/Unsee, success/failure receipts, draft and
|
|
caret preservation, pending-send exclusion, storage denial, stale registration,
|
|
loading/empty/malformed/unreachable states and hostile text. Contrast is measured
|
|
on 330 rendered samples across ten palettes and three modes. Layouts are checked
|
|
at 320, 390, 768, 1440 and 2560px. Horizontal scrolling is intentional within the
|
|
dense table; the page itself must not overflow.
|
|
|
|
Conversation tests run the real board routes over temporary repository-layout
|
|
session files. They cover full-length answers, collapsed tools and thinking,
|
|
hostile content rendered inert, malformed-line and reconcile markers, forks, and
|
|
the return flow: a send from the view, a tool call and a delayed result while a
|
|
draft is typed, a peer message, a 4.5-million-character answer split into
|
|
continuation parts, and a relaunch mid-turn.
|
|
|
|
Bus tests (`bus.test.mjs`, `bus-browser.test.mjs`) run the routes and the
|
|
views against an in-process broker read through its reader session, so every
|
|
row has the broker's own shape. `humanCall` runs against a stub CLI script.
|
|
They cover each view, the gap labels, inert hostile text, Copy, the stale
|
|
fallback and each failure title, and that no route writes. They never start
|
|
the human CLI against a live bus.
|
|
|
|
Shell tests (`shell.test.mjs`) check the copies, the font hashes and the kept
|
|
strings, and in the browser at 400px: System mode under an emulated dark and
|
|
light OS, the empty, stale and refused states of the board and the bus views
|
|
(a board refusal with the inspector open and a project picked, then a failed
|
|
read, and a refusal with a conversation open), not found, the palette, the toast,
|
|
keyboard and visible focus, the section strip at 360 and 400px, no sideways
|
|
scroll, no script error and no request other than GET. A second test walks
|
|
the Board, Inbox, Tasks, Agents and a decision trail through loading (a held
|
|
read), normal, a failed read over kept rows, and a refusal by the bus
|
|
(`human-required`) and for want of one (`no-bus-host`). It then checks that a
|
|
refusal empties the palette and the Inbox count, from a view, from the
|
|
palette's own reads and from the board page, and that palette labels are text.
|
|
|
|
No root CI workflow is configured for this package. These local tests are not a
|
|
claim of CI, deployment, live-seat delivery or user acceptance.
|
|
|
|
## User test and rollback
|
|
|
|
Gate E belongs to Jason on Monday 2026-09-14. Use Console to find who is waiting,
|
|
open a registered repo seat, reply and see its next answer after a scan. Mark a
|
|
completion Seen and find it again in Seen. Record any reason to open the board's
|
|
own page or a repo seat terminal in DEFERRED.md. Pass is Jason's end-of-day
|
|
say-so, not a test result. Keep #1507 open pending that ruling.
|
|
|
|
Rollback requires only stopping the WebUI and using the unchanged board at 7331.
|
|
Revert the scoped WebUI commit to remove it; there is no data migration to undo.
|
|
Seen and reply actions already taken belong to the board and are not rolled back.
|