349 lines
26 KiB
Markdown
349 lines
26 KiB
Markdown
# Row 53 (#1542): Console tokens and shell restyle
|
||
|
||
Dewey, 2026-10-10. Brief: `docs/plans/2026-10-10_design-implementation.md`,
|
||
section "Console tokens and shell restyle" (origin/refactor a81915e8), with
|
||
`docs/design/IMPLEMENTING.md` sections "Tokens", "Icons and the mark",
|
||
"Components", "Strings", "Boundaries" and "Acceptance". Lead decision 81
|
||
(Harbor following the system setting by default, the Relay placeholder mark
|
||
stays, no Decision Seen from the web).
|
||
|
||
Built on 923957e2; no file under `packages/webui`, `docs/design` or the
|
||
test scripts changed between it and the gate base 8a7871ff (only
|
||
`scripts/mosaic-task.mjs`, row 56). Candidate: 16 files, uncommitted in the
|
||
canonical checkout, listed in `candidate-manifest.sha256` beside this file
|
||
(round 1 manifest sha256 `75569953c1a2cd7b7266208d6e6607290335f1bfe0cf94d96ed224e480a1dda8`;
|
||
rounds 2 and 3 below; round 3's is current).
|
||
|
||
## Round 3 (answers #1542 comments 27163 and 27165)
|
||
|
||
Scope from Sage: two fixes, each with a test. 1: a board 403 followed by a
|
||
503 or 500 must not bring back the refused scan's rows or waiting card
|
||
(Filbert C4, Darkwing P9). 2: a board 403 must clear an open conversation's
|
||
history (Darkwing P10).
|
||
|
||
Round 3 candidate: the same 16 files, manifest sha256
|
||
`2562c05d4a92f90096c0fe68ef8c2adfa7244fa547f3390155f5bee5cce64cba`
|
||
(`candidate-manifest.sha256` beside this file, replacing round 2's
|
||
`78c3aef9`). Three files changed from round 2: `README.md`,
|
||
`src/public/app.js`, `tests/shell.test.mjs`. The round 2 to round 3 delta is
|
||
`round3.patch` beside this file. Applied to the round 2 files, it reproduces
|
||
the manifest (16 OK, checked). Base for the gate: 03969219.
|
||
|
||
### Fixes
|
||
|
||
| Item | Change | Test |
|
||
|---|---|---|
|
||
| Filbert C4, Darkwing P9: a 403 then a 500 or 503 showed the refused scan's table and waiting card again | `error()` now draws the empty board whenever it holds no data, not only on a refusal. It clears `#waiting`, `#seen` and `#sessions`, sets the three counts to `–`, closes the inspector, and says why in the tree, the footer and `#status`: "the board refused the read" or "the read failed". | Shell browser test: after the refusal, `brd.unavailable()` (503) and a refresh. The banner is `banner err` with "No board data loaded."; `#board-view` shows again; no session row, open or history button, waiting card or seen card; the three counts are `–`; the footer and `#fresh-board` read "Board not read: the read failed", `#status` "Not read: the read failed", the tree "Not read: the read failed.". Screenshot `board-refused-then-failed-400`. |
|
||
| Darkwing P10: a 403 with History open left `#conversation` and the old `#conv-log` messages | On a refusal with a conversation open, `error()` empties `#conv-log` and calls `closeConversation()`, which hides the panel and its reply form. | A session with `registered` set; History open; the fixture message "Fixture history line" in `#conv-log` and the reply form shown. After a 403: `#conversation` hidden, no `has-conversation` on `<body>`, `#conv-log` has no child, `#conv-form` not visible, the board hidden and the refusal state shown. Then recovery. |
|
||
|
||
Test fixtures in `shell.test.mjs`'s stub board: `one()` adds a third
|
||
session that is seen (so the seen card is tested), `registered()` answers
|
||
one live session with a conversation, `unavailable()` answers 503, and with
|
||
status 200 the stub answers `/api/conversations` and `/api/conversation`
|
||
with one fixture page. The new assertions sit in the "shell in a browser"
|
||
test, where the board refusal state already lives.
|
||
|
||
### Also answered
|
||
|
||
| Item | Done |
|
||
|---|---|
|
||
| Filbert T7: the counts after a refusal | Asserted: `#waiting-count`, `#seen-count` and `#session-count` are `–` after the 403 and after the 503 that follows it. |
|
||
| Filbert N7 (my call): the project filter on a refusal | Tested, not changed: a project is picked before the refusal (`aria-pressed` true), and after recovery the "all" filter is the pressed one. |
|
||
| README | The refusal line in the command bar section now names the cards, the project filter, the empty board on a later failure and the closed conversation. The shell tests line names the two new cases. |
|
||
|
||
### Changes in behaviour beyond the two fixes
|
||
|
||
- A failed read before the first scan (no data yet, not a refusal) now draws
|
||
the empty board with "Not read: the read failed" in the tree, footer and
|
||
`#status`, where round 2 left the skeleton rows, "Loading board…" and
|
||
"Loading projects…" under the error banner. This is
|
||
the same code path as the C4 fix; the banner was already shown in both.
|
||
- `error()` no longer calls `convForm()`. With P10, a refusal closes the
|
||
conversation and `convForm()` does nothing without one; a non-refusal
|
||
failure with no data never had an open conversation. The `error-noconvform`
|
||
mutant survived for that reason and is dropped.
|
||
|
||
### Round 3 mutants
|
||
|
||
Each applied to a scratch copy of the round 3 tree, then `shell.test.mjs`
|
||
run (`~/dewey-scratch/r53b/r3/mutants.py`, outputs in `r3/out/`). 13 of 13
|
||
killed, every one by the "shell in a browser" test.
|
||
|
||
| Mutant | Change | Killed at |
|
||
|---|---|---|
|
||
| c4-no-clear | no-data branch leaves the cards and the table | no rows or cards after the 503 (11, expected 0) |
|
||
| c4-keep-sessions | the table kept | same (7) |
|
||
| c4-keep-waiting | the waiting cards kept | same (2) |
|
||
| c4-keep-seen | the seen cards kept | same (2) |
|
||
| c4-refused-only | the empty board only on a refusal (round 2) | footer "refused" after the 503 |
|
||
| c4-footer-refused | footer always says "refused" | footer after the 503 |
|
||
| c4-status-refused | `#status` always says "refused" | `#status` after the 503 |
|
||
| c4-tree-refused | the tree always says "refused" | tree after the 503 |
|
||
| error-keeps-counts | the counts left as they were | `#waiting-count` after the 403 |
|
||
| error-noinspect | the inspector left open | `#inspector` hidden after the 403 |
|
||
| error-keeps-project | a 403 keeps the project filter | "all" filter pressed after recovery |
|
||
| p10-no-close | the conversation left open | `#conversation` hidden after the 403 |
|
||
| p10-no-clear | the panel closed but the history kept | `#conv-log` empty after the 403 |
|
||
|
||
### Round 3 gate
|
||
|
||
Worktree at 03969219 plus the 16 round 3 files (`sha256sum -c` of the
|
||
manifest: 16 OK), node_modules linked from the checkout, TMPDIR in
|
||
scratch, `DOCKER_HOST` pointed at a missing socket, suites sequential,
|
||
2026-10-10T19:13:43Z to 19:18:19Z. `core.hooksPath` unset. Script
|
||
`~/dewey-scratch/r53b/r3/gate.sh`, outputs `~/dewey-scratch/r53b/r3/gate/out/`.
|
||
|
||
| Suite | Result |
|
||
|---|---|
|
||
| `node --test 'packages/webui/tests/*.test.mjs'` | 27 pass, 0 fail |
|
||
| node suites of business, bus, cli, control-board, conversation, discord, ledger, mosaic, queue, runs, seat, tasks | 60, 67, 66, 124, 182, 178, 78, 69, 148, 41, 19, 51 pass; 0 fail in each |
|
||
| `node docs/design/tools/build-tokens.mjs --check` | rc 0, "tokens.css is current" |
|
||
| test-auth | 15 passed, 0 failed |
|
||
| test-conductor | 17 passed, 0 failed |
|
||
| test-config | 24 passed, 0 failed |
|
||
| test-discord | 66 passed, 0 failed |
|
||
| test-extension-package | 18 passed, 0 failed |
|
||
| test-foundation | 44 passed, 0 failed |
|
||
| test-queue | 27 passed, 0 failed |
|
||
| test-release, no Docker | 4 passed, 0 failed (state-machine cases skipped: daemon unavailable) |
|
||
| test-task, no Docker | 26 passed, 0 failed (adapter seam, workspace and live cases skipped) |
|
||
|
||
Round 2's gate had Docker for test-release and test-task (14 and 98).
|
||
Round 3 changes nothing those Docker cases run; Sage's gate rerun covers them.
|
||
|
||
### Not in this round
|
||
|
||
Darkwing note 3, the refresh-timer focus race in `bus.js` `render()`: it is
|
||
in HEAD, so per Sage it goes to row 54 with its own test, named there as a
|
||
fix to HEAD behaviour beside the H1 `keep()`/`restore()` fix.
|
||
|
||
## Round 2 (answers #1542 comments 27143 and 27148)
|
||
|
||
Round 2 candidate: the same 16 files, manifest sha256
|
||
`78c3aef963b7e35500c9f8afa6341057f1a6372ced70915d175e507b5fac8406`
|
||
(`candidate-manifest.sha256` beside this file, replacing round 1's
|
||
`75569953`). Five files changed from round 1: `README.md`,
|
||
`src/public/app.js`, `src/public/bus.js`, `src/public/shell.css`,
|
||
`tests/shell.test.mjs`. The round 1 to round 2 delta is
|
||
`round2.patch` beside this file. Base for the gate: 2d4d4e7d (nothing under
|
||
`packages/webui`, `docs/design` or `scripts` changed since 8a7871ff).
|
||
|
||
### Blocking items
|
||
|
||
| Item | Change | Test |
|
||
|---|---|---|
|
||
| Filbert C1, Darkwing 1: a board 403 left the inspector, project tree, counts, footer and `#status` "Scanned" line | `error()` on a 403 drops `data`, `selected` and `project`, closes the inspector, and sets the tree to "Not read: the board refused the read.", the three counts to `–`, the footer to "Board not read: refused" and `#status` to "Not read: the board refused the read". The next good read restores them through `accept()` and `render()`. | Shell browser test opens the inspector and checks the footer "waiting 1 · working 1" and two tree counts before the refusal; after it, the inspector is hidden, `<body>` has no `has-inspector`, the tree has no `.count` or `[data-project]`, the footer and `#fresh-board` read "Board not read: refused", `#status` has no "Scanned". After recovery the footer is back. |
|
||
| Filbert C3: the freshness line called a refusal "stale" | `fresh()` with no data: "Board not read: refused", "Board not read: the read failed" or "Board not read yet". | Same test: `#fresh-board` equals "Board not read: refused". |
|
||
| Filbert C2, Darkwing 2 and note 6: a bus refusal left the Inbox count, BLOCKING chip and palette rows | `forget()` clears `last` and the Inbox count on any refusal: in the view's read, in the board route's inbox read (was `catch {}`), and in `pkOpen`'s refetch, which then redraws the section list. A refused read anywhere drops every kept bus read, so the pre-refusal rows can't come back as stale on a later failure either (Darkwing's P7). The bus freshness line reads "Bus not read: refused". | Five-states test: `#sections` first reads "Inbox 1 1 blocking"; after the refusal it has no `.count` or `.tag-block`, and Ctrl+K lists only the four views. A second step refuses only `pkOpen`'s own refetch and checks the kept tasks and the count are gone. On the board page a `no-bus-host` refusal of the board route's inbox read drops the count and the palette shows views only. |
|
||
| Filbert T1, Darkwing 4 (M01): only 403 was tested as a refusal | none needed | The five-states refusal step runs for `human-required` (403) and `no-bus-host` over kept rows: banner code, 0 rows, "Nothing to show.", "Bus not read: refused", no section counts, then recovery. |
|
||
| Darkwing 3: tests with the inspector open | as C1 | as C1 |
|
||
| Darkwing 5 (M08): palette labels with markup | none needed | The inbox question is `<img src=x onerror="errors.push(1)" id="pk-q">Push?` and a task title `<b id="pk-t">Bold</b> task`. The palette lists views plus three items, no `img` or `b` in the list, the labels equal the literal text, and `window.errors` stays empty. |
|
||
| Sage: the section list below 760px | The deviation is withdrawn. Below 760px `.s1-sections` is one strip, `flex-wrap:nowrap; overflow-x:auto`, items `flex:none`; it scrolls inside itself, so row 54's three sections add no rows. The command bar keeps its four controls on one row. | At 360px and 400px: every section link has one `offsetTop`, the list's computed `overflow-x` is `auto`, no label wraps, and the page has no sideways scroll. |
|
||
|
||
### Non-blocking items
|
||
|
||
| Item | Done |
|
||
|---|---|
|
||
| Filbert T2 (`accept()` keeps `failed`) | Test: a failed refresh, then a good one; `#fresh-board` no longer says "stale". |
|
||
| Filbert T3 (clipboard-denied toast stays) | Test: after 3 s the error toast's class is still `toast on err`. |
|
||
| Filbert T4 (empty states' second line) | Each bus empty state and the trail's is matched whole, both lines, anchored. The board's was already. |
|
||
| Filbert T5 (nav wrap untested) | Covered by Sage's strip test. |
|
||
| Filbert T6 (Ctrl+Alt+K) | Pinned: Ctrl+Alt+K opens nothing. Only Ctrl+K and Cmd+K open the palette. |
|
||
| Filbert N1 (local board time, UTC bus time) | Both are UTC now: "Board scanned 18:01:02 UTC (4s ago)". Test matches `^Board scanned \d\d:\d\d:\d\d UTC`. |
|
||
| Filbert N2 (strings counted across files) | The strings test counts each string per file, with the expected count (`No task ${txt(ref)} in this business.` twice in bus.js; "Nothing is waiting on you." once in app.js and once in bus.js). |
|
||
| Filbert N3, round 1 pkback | Unchanged: equivalent in Chromium; the listener stays for other browsers. |
|
||
| Darkwing note 3, M09 (focus offset) | Test: `#cmdk`'s `outline-offset` is 2px. Table rows keep their inset ring (`-2px`), unchanged and untested. |
|
||
| Darkwing note 4 (toast live region `display:none` before the first copy) | The toast is always rendered; while off it is empty and visually hidden (`clip-path: inset(50%)`, 1px box), so it stays in the accessibility tree. Test: before the first copy it is not `display:none`, is visible, is empty and is at most 1px. |
|
||
| Darkwing note 7, M16 (tabular numbers) | Test: `font-variant-numeric` is `tabular-nums` on `#fresh` and `#session-count`. |
|
||
| Darkwing M07 (toast text escaping) | Not added. The toast text is `Copied: <command>`, the commands come from validated refs, and `toast()` passes the text through `esc()`. |
|
||
| Darkwing note 2 (freshness hidden below 760px) | Unchanged. The command bar has no room at 360px, and the stale and refusal banners still state it in the view. Left for row 54 or the design session. |
|
||
| Darkwing note 5 (Seen 403 wording, prose in `<code>`) | Unchanged. A Seen 403 now also drops the board data, the same as a read 403; since only control-board's Host/Origin check gives it, the read would be refused too. |
|
||
|
||
### Round 2 mutants
|
||
|
||
Each applied to a scratch copy of the round 2 webui, then `shell.test.mjs`
|
||
run (`mut/tools/mutants.py`, `run.sh`). 20 of 20 killed.
|
||
|
||
| Mutant | Change | Killed by |
|
||
|---|---|---|
|
||
| C1-keep-data | a board 403 keeps `data` | shell browser test |
|
||
| C1-keep-inspector | a board 403 keeps `selected` and the inspector | shell browser test |
|
||
| C1-keep-footer | a board 403 leaves the footer | shell browser test |
|
||
| C3-fresh-wording | "stale" wording on a refusal | shell browser test |
|
||
| C2-render-forget | the view's refused read doesn't `forget()` | five-states test |
|
||
| C2-pk-forget | `pkOpen`'s refused refetch doesn't `forget()` | five-states test |
|
||
| C2-board-forget | the board route's refused inbox read doesn't `forget()` | five-states test |
|
||
| C2-forget-keeps-count | `forget()` clears `last` but not the count | five-states test |
|
||
| M01-refusal-403-only | `refusal()` only for 403 | five-states test |
|
||
| M08-pk-label-raw | palette label set as HTML | five-states test |
|
||
| nav-wraps | the round 1 wrapping section list | shell browser test (strip) |
|
||
| toast-display-none | the round 1 `display:none` toast | shell browser test |
|
||
| M09-offset-0 | focus `outline-offset: 0` | shell browser test |
|
||
| M16-no-tabular | no `tabular-nums` | shell browser test |
|
||
| N1-local-time | board time in local time | shell browser test |
|
||
| N2-one-copy | one copy of the duplicated task string changed | strings test |
|
||
| T6-ctrl-alt-k | Ctrl+Alt+K opens the palette | shell browser test |
|
||
| T3-err-toast-hides | the error toast closes after 2.6 s | shell browser test |
|
||
| T2-stale-sticks | `accept()` keeps `failed` | shell browser test |
|
||
| bus-fresh-refused | bus refusal shows the round 1 freshness wording | shell browser test |
|
||
|
||
### Round 2 gate
|
||
|
||
Worktree at 2d4d4e7d plus the 16 round 2 files (`sha256sum -c` of the
|
||
manifest: all OK), node_modules linked from the checkout, TMPDIR in scratch,
|
||
suites sequential, 2026-10-10T18:15:17Z to 18:19:16Z. Outputs:
|
||
`~/dewey-scratch/r53b/gate/out/`.
|
||
|
||
| Suite | Result |
|
||
|---|---|
|
||
| `node --test 'packages/webui/tests/*.test.mjs'` | 27 pass, 0 fail |
|
||
| `node docs/design/tools/build-tokens.mjs --check` | rc 0, "tokens.css is current" |
|
||
| test-auth | 15 passed, 0 failed |
|
||
| test-conductor | 17 passed, 0 failed |
|
||
| test-config | 24 passed, 0 failed |
|
||
| test-discord | 66 passed, 0 failed |
|
||
| test-extension-package | 18 passed, 0 failed |
|
||
| test-foundation | 44 passed, 0 failed |
|
||
| test-queue | 27 passed, 0 failed |
|
||
| test-release | 14 passed, 0 failed |
|
||
| test-task, Docker present | 98 passed, 0 failed |
|
||
|
||
The round 1 packet follows unchanged, except where rounds 2 and 3 supersede it
|
||
(the nav deviation is withdrawn; the manifest above replaces `75569953`).
|
||
|
||
# Round 1 packet
|
||
|
||
## What changed
|
||
|
||
All in `packages/webui`. No new endpoint, read, write or dependency.
|
||
|
||
| File | Change |
|
||
|---|---|
|
||
| `src/public/tokens.css` | New. Byte copy of `docs/design/tokens.css`. |
|
||
| `src/public/icons.svg` | New. Byte copy of `docs/design/icons.svg`: 17 icons and the `mark` symbol. |
|
||
| `src/public/shell.css` | New. The components on top of the tokens: command bar, freshness line, section list with icons, dense table with tabular numbers, inspector, status mark (glyph plus word), class chip and BLOCKING chip, copy command, toast, Ctrl+K palette, empty, not found, banners (`err`, `ref`), skeleton rows, `@font-face` for JetBrains Mono, `--r` 6px and `--r-lg` 10px use, focus rings, the 400px layout. Loaded last. |
|
||
| `src/public/assets/fonts/jetbrains-mono-{400,500,700}.woff2`, `jetbrains-mono-OFL.txt`, `jetbrains-mono-sources.txt` | JetBrains Mono 2.304 from the official release archive (URL and archive sha256 in the sources file), each file's sha256, and the OFL text from the archive. |
|
||
| `src/public/index.html` | Loads `tokens.css` after `shared/app.css` and `shell.css` last; no `data-mode` on `<html>`; the mark from the sprite in the wordmark; the Ctrl+K button, freshness line, System option, banner error element, board refusal empty state, board skeleton, toast and palette `<dialog>`. |
|
||
| `src/public/app.js` | `theme()` sets no property inline: it sets `data-palette`, and `data-mode` unless the mode is System, which removes it. Default mode `system`. Same key `mosaic-console-appearance`. Status mark classes on the state badge. Empty states with "what would appear and where it comes from". Board freshness line. A 403 from the board fails closed (kept rows hidden, `banner ref`); any other failure keeps the rows with their scan time. |
|
||
| `src/public/bus.js` | Class and BLOCKING chips, section icons, empty states, the skeleton loading view, not found that names the address and points to the Board and Ctrl K, freshness for bus pages, a refusal (403, `not-configured`, `no-bus-host`) never showing kept rows, the toast on copy, the Ctrl+K palette, ↑/↓ between task links. |
|
||
| `src/serve.mjs` | The files map only: `tokens.css`, `shell.css`, `icons.svg`, the three mono fonts. |
|
||
| `tests/shell.test.mjs` | New: copies, font digests, strings and the write boundary; two browser tests (below). |
|
||
| `tests/serve.test.mjs` | The new static files are served with their types. |
|
||
| `tests/browser.mjs` | `ArrowUp` and `k` key codes; no text insert when a modifier is held (so Ctrl+K doesn't type `k`). |
|
||
| `README.md` | "Shell (row 53, #1542)" section; the stale/refusal rule; the verify lines. |
|
||
|
||
## Acceptance against the brief
|
||
|
||
| Brief line | Where it is checked |
|
||
|---|---|
|
||
| `tokens.css` served, `theme()` no inline properties | serve.test (type); shell browser test: `<html>` has no `style` attribute; inline mutant fails |
|
||
| `data-palette` and `data-mode` stay; System removes `data-mode`; key kept; Harbor + System default | shell browser test: fresh load has no `data-mode`, palette `harbor`, select `system`; dark and light OS give dark and light pages; picking dark sets it and stores it under `mosaic-console-appearance`; System removes it again. Mode mutant fails. |
|
||
| `--r` 6px, `--r-lg` 10px, type scale, tabular numbers | computed `--r` and `--r-lg` asserted; type scale and `font-variant-numeric: tabular-nums` in shell.css |
|
||
| JetBrains Mono 400/500/700 with sha256 and OFL | static test: shipped files equal the listed set, each digest matches, OFL heading present, `@font-face` for each weight; `document.fonts.check` in the browser |
|
||
| Icon sprite; mark in one component; Relay placeholder | `icons.svg` copy test; the mark is drawn once, in the wordmark, from `#mark` |
|
||
| Components | each rendered in the browser test and its screenshots: command bar, section list (4 icons asserted), dense table, inspector (opened by keyboard), status mark (`◐` glyph asserted), class chip (`code` action, uppercase BLOCKING asserted), copy command and toast (success and clipboard-denied), palette, not found |
|
||
| Five states on Board, Inbox, Tasks, Agents, Trail | second browser test walks each view through loading (a held read shows the skeleton), normal, error over kept rows (same row count under `banner err s1-stale`), refusal (`banner ref`, code in mono, no rows, "Nothing to show.") and back. Empty is seeded per view in the first test. |
|
||
| Browser: no script errors, no sideways scroll at 400px, keyboard ↑/↓, Enter, Esc returning focus, visible focus | `window.errors` empty at the end of both tests; `scrollWidth <= clientWidth` after every state; board ↑/↓/Enter/Esc, task-link ↑/↓, palette ↑/↓/Enter/Esc with focus back on the opener; outline ≥2px on `#cmdk`, a section link and Refresh |
|
||
| String grep test | static test: the kept strings from bus.js, app.js and index.html, no Resolve/Decide/Approve button, no non-GET method in bus.js, no script in the sprite |
|
||
| No writes | both browser tests: every request the page made is a GET |
|
||
|
||
## Tests
|
||
|
||
Outputs in `~/dewey-scratch/r53/out/`.
|
||
|
||
| Run | Result |
|
||
|---|---|
|
||
| `node --test 'tests/*.test.mjs'` in `packages/webui`, with `WEBUI_EVIDENCE` (`full-4.txt`) | 27 pass, 0 fail |
|
||
| `shell.test.mjs` alone, three repeats after the last test change (`shell-rep-{1,2,3}.txt`) | 5 pass each |
|
||
| Five-states test alone (`states-1.txt`) | pass |
|
||
|
||
Screenshots (`out/evidence-4/`, 400px unless named): `shell-board-empty`,
|
||
`-board-stale`, `-board-refused`, `-bus-empty-{inbox,tasks,agents,trail}`,
|
||
`-bus-stale`, `-bus-refused`, `-bus-inbox`, `-palette`, `-toast`,
|
||
`-bus-tasks-1440`, `-board-1440`, and `states-{board,inbox,tasks,agents,trail}-{loading,normal,stale,refused}`
|
||
(board: loading and normal). I looked at each.
|
||
|
||
## Mutations
|
||
|
||
Each applied to a scratch copy of the candidate, then `shell.test.mjs` run
|
||
(`mutbin/run.sh`, outputs `out/mut-*.txt`).
|
||
|
||
| Mutant | Result |
|
||
|---|---|
|
||
| tokens: a comment appended to the `src/public/tokens.css` copy | fails: "tokens.css differs from docs/design/tokens.css" |
|
||
| inline: `theme()` sets one property inline again | fails: `<html>` style is `--canvas: #fff;` |
|
||
| mode: System sets `data-mode="light"` instead of removing it | fails: `true !== false` |
|
||
| refusal: drop `if (refusal(err)) throw err` (refusal shows kept rows) | fails: no `.banner.ref` |
|
||
| focus: `:focus-visible{outline:none}` appended to shell.css | fails: `#cmdk focus ring` |
|
||
| foot: the side column's top and height rule removed (760px and up) | fails: column bottom 930 against 900 |
|
||
| post: `method: 'POST'` in `fetchRead` | fails: static check and the GET-only browser check |
|
||
| loading: no loading view on navigation | fails: no `#bus-view .skel-rows[aria-busy=true]` (five-states test) |
|
||
| skel: no board skeleton | fails: no `#sessions .skel-rows[aria-busy=true]` (five-states test) |
|
||
| pkback: drop the `b.focus()` in the palette's close listener | survives: Chromium's native `<dialog>` close puts focus back on the opener, so the listener is redundant there and the mutant is equivalent in this browser. Kept for browsers that don't. |
|
||
|
||
## Gate
|
||
|
||
Worktree at 8a7871ff plus the 16 candidate files (`sha256sum -c` of the
|
||
manifest: all OK), node_modules linked from the checkout, TMPDIR in scratch,
|
||
suites sequential, 2026-10-10T17:24:05Z to 17:28:05Z. Outputs:
|
||
`~/dewey-scratch/r53/gate/out/`.
|
||
|
||
| Suite | Result |
|
||
|---|---|
|
||
| `node --test 'packages/webui/tests/*.test.mjs'` | 27 pass, 0 fail |
|
||
| `node docs/design/tools/build-tokens.mjs --check` | rc 0, "tokens.css is current" |
|
||
| test-auth | 15 passed, 0 failed |
|
||
| test-conductor | 17 passed, 0 failed |
|
||
| test-config | 24 passed, 0 failed |
|
||
| test-discord | 66 passed, 0 failed |
|
||
| test-extension-package | 18 passed, 0 failed |
|
||
| test-foundation | 44 passed, 0 failed |
|
||
| test-queue | 27 passed, 0 failed (verify and render --check skip outside the canonical root) |
|
||
| test-release | 14 passed, 0 failed |
|
||
| test-task, Docker present | 98 passed, 0 failed |
|
||
|
||
An earlier gate run on 3265383f with the manifest before the five-states
|
||
test was also green (26 webui tests); it is superseded by this one.
|
||
|
||
## Decisions and deviations from the design
|
||
|
||
- **Bus refusal fails closed.** Before this row a 403 from the bus still
|
||
showed kept rows under a stale banner. Now a refusal (403,
|
||
`not-configured`, `no-bus-host`) shows only the failure: "Console shows
|
||
nothing rather than a guess". Other failures keep the rows, labelled with
|
||
their read time. The board does the same for its 403.
|
||
- **Footer is a sticky status bar from 760px.** With the side column
|
||
sticky under the command bar, a page taller than the viewport slid the
|
||
column under the bar at 1440×900. The footer now sits at the bottom,
|
||
the column fills the space between, and the browser test checks the
|
||
edges on a short page and on a long one scrolled to the end.
|
||
- **Nav wraps below 760px** (withdrawn in round 2: the section list is
|
||
one strip that scrolls inside itself, per IMPLEMENTING.md) instead of
|
||
scrolling sideways, and the command bar keeps Palette, Appearance, Refresh and Pause on one row down
|
||
to 360px.
|
||
- **Manrope 800 renders as 700**: only 400 to 700 are shipped (row 53
|
||
adds no Manrope weight). Visible on the large headings only.
|
||
- **No business pill** in the command bar: there is one business and no
|
||
business read yet; the Business view is row 54.
|
||
- **Not found keeps the existing string** "No page at this address." and
|
||
adds the address, a Board link and Ctrl K.
|
||
- **`#copy-status` lost `role="status"`.** The toast is the live region
|
||
now; both announcing the same line would read it twice. The status line
|
||
still shows the text.
|
||
- **Class chip colours** use the design's four classes; an unknown class
|
||
gets the plain chip.
|
||
|
||
## Not done here (follow-ups)
|
||
|
||
- Row 54 (#1543) adds Queue, Business and Settings and will reuse these
|
||
components; both rows change `app.js`, so 54 starts after 53 lands.
|
||
- The pkback mutant above: a test in a browser without native dialog
|
||
focus restore would need another engine; none is in the repo.
|
||
|
||
## Boundaries kept
|
||
|
||
Commits: none (Sage commits). No change outside `packages/webui` and this
|
||
directory. `docs/design/` read only. The Q14 frozen paths untouched. No
|
||
request to the tracker from node. No new dependency. Scratch worktrees
|
||
removed after use; `core.hooksPath` unset.
|