review(53): Darkwing round 1, request changes (#1542 comment 27148)

Refusal leaves board data in the inspector, project tree, footer and
scan line, and bus data in the Inbox count and the Ctrl+K palette.
Gate green, 13 of 20 mutants killed, fonts match the upstream archive.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-10 12:59:06 -05:00
co-authored by Claude Opus 5.5
parent 71af13092b
commit 70768fe548
63 changed files with 2057 additions and 0 deletions
@@ -0,0 +1,16 @@
d7b2b6958857eb97902c46480bf7e3786966cae9436e3763512da526fa256da3 packages/webui/README.md
b9b162ebf6ff1c690d2731c4b63ebfb36d12d3176a0be8fdc1a449b706432fd8 packages/webui/src/public/app.js
a9cb1cd82332b23a47e3a1239d25d13c86d16c4220695e34b243effa999f45f2 packages/webui/src/public/assets/fonts/jetbrains-mono-400.woff2
086c48dfbea9ddaff1320f7e09399b8e2924e88ce67453721255db3bdbb5a353 packages/webui/src/public/assets/fonts/jetbrains-mono-500.woff2
c503cc5ec5f8b2c7666b7ecda1adf44bd45f2e6579b2eba0fc292150416588a2 packages/webui/src/public/assets/fonts/jetbrains-mono-700.woff2
30f0c136e3c88e422d0791acd97238870f9054a9729bc34cf2ff0d4ed8cac4ad packages/webui/src/public/assets/fonts/jetbrains-mono-OFL.txt
f6f89aaaa6a02e9a16020de26fb6a664f1da152e0b0413248da2d9f6b68395cc packages/webui/src/public/assets/fonts/jetbrains-mono-sources.txt
65d4ea9386aca7ed57107695e13cf91c29d15ba44d87cc085d2a05732201b03e packages/webui/src/public/bus.js
01b456c1ca685e69e2974d66a615b7b8736abbd51aef7a3f014bca3783710430 packages/webui/src/public/icons.svg
fbbb51e5669a1f559015e0fdebf6f450b63383070472d2461f0bce9b9723d890 packages/webui/src/public/index.html
b3ae07918fced7b0323835bc35e73fffb3c3fb0921a36e22dd2eb167997c1e69 packages/webui/src/public/shell.css
4fdb72ec24c7245584c89280415e10113dba1c4cb03d98f5570ac745ba2e95cb packages/webui/src/public/tokens.css
5a65fea95e6225f812c196b0199f3f6562a60e523da685b255f749f63b480c64 packages/webui/src/serve.mjs
6f68f67e99d5e832c0ba219911201b8adf08505e234647f2d9eac7292d55f50f packages/webui/tests/browser.mjs
713c86bd6b1d1b27949bbae071f1bdf1f7d744f84b723ad5a1bdbd1e8adc5fb6 packages/webui/tests/serve.test.mjs
f0ce4368cf0f0caab11aeb0f603c90d12066a40c33c51a20b53badd451d66cef packages/webui/tests/shell.test.mjs
@@ -0,0 +1,9 @@
# 2026-10-10T17:40:36Z font provenance check (Darkwing)
archive sha256: 6f6376c6ed2960ea8a963cd7387ec9d76e3f629125bc33d1fdcd7eb7012f7bbf
Archive sha256: 6f6376c6ed2960ea8a963cd7387ec9d76e3f629125bc33d1fdcd7eb7012f7bbf
fonts/webfonts/JetBrainsMono-Regular.woff2 archive=a9cb1cd82332b23a47e3a1239d25d13c86d16c4220695e34b243effa999f45f2 shipped=a9cb1cd82332b23a47e3a1239d25d13c86d16c4220695e34b243effa999f45f2 match
fonts/webfonts/JetBrainsMono-Medium.woff2 archive=086c48dfbea9ddaff1320f7e09399b8e2924e88ce67453721255db3bdbb5a353 shipped=086c48dfbea9ddaff1320f7e09399b8e2924e88ce67453721255db3bdbb5a353 match
fonts/webfonts/JetBrainsMono-Bold.woff2 archive=c503cc5ec5f8b2c7666b7ecda1adf44bd45f2e6579b2eba0fc292150416588a2 shipped=c503cc5ec5f8b2c7666b7ecda1adf44bd45f2e6579b2eba0fc292150416588a2 match
OFL.txt archive=30f0c136e3c88e422d0791acd97238870f9054a9729bc34cf2ff0d4ed8cac4ad shipped=30f0c136e3c88e422d0791acd97238870f9054a9729bc34cf2ff0d4ed8cac4ad match
tokens.css equal to docs/design
icons.svg equal to docs/design
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# Row 53 gate: packages/webui node suite, build-tokens --check, then every scripts/test-*.sh with Docker unreachable.
export TMPDIR=~/darkwing-scratch/r53a/tmp DOCKER_HOST=unix:///nonexistent.sock
cd ~/darkwing-scratch/r53a/cand
O=~/darkwing-scratch/r53a/out
: > $O/summary.txt
echo "start $(date -u +%FT%TZ)" >> $O/summary.txt
(cd packages/webui && env -u NODE_TEST_CONTEXT node --test 'tests/*.test.mjs') > $O/node-webui.txt 2>&1; e=$?
echo "node-webui exit=$e $(grep -E '^ℹ (pass|fail)' $O/node-webui.txt | tr '\n' ' ')" >> $O/summary.txt
node docs/design/tools/build-tokens.mjs --check > $O/build-tokens.txt 2>&1; e=$?
echo "build-tokens --check exit=$e $(tail -1 $O/build-tokens.txt)" >> $O/summary.txt
for f in scripts/test-*.sh; do
s=$(basename $f .sh)
$f > $O/$s.txt 2>&1; e=$?
echo "$s exit=$e $(grep -E 'passed, [0-9]+ failed' $O/$s.txt | tail -1)" >> $O/summary.txt
done
echo "end $(date -u +%FT%TZ)" >> $O/summary.txt
echo DONE >> $O/summary.txt
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index f5389ee6..5ea891ad 100644
--- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js
@@ -368,7 +368,7 @@
}
const failTitle = err => err.status === 403 ? 'Bus refused the read' : ['not-configured', 'no-bus-host'].includes(err.code) ? 'No bus to read' : 'The read failed';
// A refusal fails closed: no kept data, the code in mono. Any other failure keeps the last read, labelled with its time.
- const refusal = err => err.status === 403 || ['not-configured', 'no-bus-host'].includes(err.code);
+ const refusal = err => err.status === 403;
const failedView = err => `${head(failTitle(err))}<div class="banner ${refusal(err) ? 'ref' : 'err'}" role="alert"><p>${refusal(err) ? `<b>refused: <code>${txt(err.code)}</code>.</b> ` : ''}${why(err)} Console shows nothing rather than a guess. Nothing was written.</p>${retry}</div>${empty('Nothing to show.', refusal(err) ? 'The view stays empty until the problem above is fixed.' : 'Read again, or come back when the bus answers.')}`;
const staleBanner = err => `<div class="banner err s1-stale" role="alert"><p><b>The last read failed.</b> ${why(err)} This is what was read before, ${when(readAt)}. Nothing was changed.</p>${retry}</div>`;
// Freshness line in the command bar for bus pages.
@@ -0,0 +1,36 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (7051.173711ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7270.330094ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8305.819932ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (465.310562ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (142.624471ms)
✔ bus refusals map to statuses and never carry a path or a stack (60.934334ms)
✔ humanCall speaks the human transport protocol without blocking the server (2813.055778ms)
✔ busReader takes --business, else the live bus host, else refuses (488.166908ms)
✔ humanCall kills a transport that runs past its timeout (538.051321ms)
✔ serve refuses a --business value that is not a business id (500.293546ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5952.997676ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3085.264687ms)
✔ conversation view: a newer session with no readable history keeps the marker (1717.994807ms)
✔ conversation view: seats without history say so and offer no reply (1097.051106ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6156.19174ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56811.320882ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6077.150559ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (25388.059358ms)
✔ loopback host and board origin fail closed (10.456016ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (214.245672ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (234.702735ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1644.897153ms)
✔ tokens.css and icons.svg are byte copies of docs/design (3.908141ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.212866ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (2.272588ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7192.367132ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3294.146704ms)
ℹ tests 27
ℹ suites 0
ℹ pass 27
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 57463.713678
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/app.js b/packages/webui/src/public/app.js
index 1bd88cf2..c941e531 100644
--- a/packages/webui/src/public/app.js
+++ b/packages/webui/src/public/app.js
@@ -303,7 +303,7 @@
// A refusal (403) fails closed: the last data is hidden, not shown as current. Any other failure keeps the last data with its scan time.
function error(err) {
failed = true; $('error').hidden = false;
- const refused = err.status === 403;
+ const refused = false;
$('error').className = refused ? 'banner ref' : 'banner err';
$('board-refused').hidden = !refused; document.body.classList.toggle('board-refused', refused);
$('error').innerHTML = refused
@@ -0,0 +1,59 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5906.359439ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6421.571921ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7790.380244ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (319.811693ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (141.932338ms)
✔ bus refusals map to statuses and never carry a path or a stack (135.266704ms)
✔ humanCall speaks the human transport protocol without blocking the server (2526.57828ms)
✔ busReader takes --business, else the live bus host, else refuses (389.079573ms)
✔ humanCall kills a transport that runs past its timeout (531.702165ms)
✔ serve refuses a --business value that is not a business id (225.100624ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5885.859729ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2943.465302ms)
✔ conversation view: a newer session with no readable history keeps the marker (1599.972822ms)
✔ conversation view: seats without history say so and offer no reply (1304.01305ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5738.474826ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56134.123141ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5780.259394ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24979.223692ms)
✔ loopback host and board origin fail closed (11.709394ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (216.387749ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (108.234726ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1702.907874ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.588661ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.421399ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (2.1794ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4814.72897ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3632.166067ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 56812.431306
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4814.72897ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ 'banner err'
- 'banner ref'
^
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:140:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'banner err',
expected: 'banner ref',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/app.js b/packages/webui/src/public/app.js
index 1bd88cf2..a5cf03b4 100644
--- a/packages/webui/src/public/app.js
+++ b/packages/webui/src/public/app.js
@@ -305,7 +305,7 @@
failed = true; $('error').hidden = false;
const refused = err.status === 403;
$('error').className = refused ? 'banner ref' : 'banner err';
- $('board-refused').hidden = !refused; document.body.classList.toggle('board-refused', refused);
+ $('board-refused').hidden = !refused;
$('error').innerHTML = refused
? `<p><b>The board refused the read.</b> refused: <code>${esc(err.message)}</code> Board: ${esc(board)}. Console shows nothing rather than a guess. Use Refresh to try again.</p>`
: `<p><b>${esc(err.message)}</b> Board: ${esc(board)}. ${data ? `Showing last known data.${data.generatedAt ? ` Scanned ${esc(data.generatedAt)}.` : ''}` : 'No board data loaded.'} Use Refresh to try again.</p>`;
@@ -0,0 +1,56 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5843.810902ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6581.265927ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8986.282108ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (1059.307807ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (207.046686ms)
✔ bus refusals map to statuses and never carry a path or a stack (70.701032ms)
✔ humanCall speaks the human transport protocol without blocking the server (2229.666935ms)
✔ busReader takes --business, else the live bus host, else refuses (349.885509ms)
✔ humanCall kills a transport that runs past its timeout (528.250357ms)
✔ serve refuses a --business value that is not a business id (216.252848ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (6331.227648ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3044.074164ms)
✔ conversation view: a newer session with no readable history keeps the marker (1387.057031ms)
✔ conversation view: seats without history say so and offer no reply (1032.915675ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6249.684157ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56312.695193ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5805.305368ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23253.279537ms)
✔ loopback host and board origin fail closed (9.249748ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (148.461303ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (182.583553ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1604.6308ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.446228ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.794556ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.912476ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6098.431393ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3104.411072ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 56801.360852
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6098.431393ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
'block' !== 'none'
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:142:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'block',
expected: 'none',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index f5389ee6..5496a305 100644
--- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js
@@ -369,7 +369,7 @@
const failTitle = err => err.status === 403 ? 'Bus refused the read' : ['not-configured', 'no-bus-host'].includes(err.code) ? 'No bus to read' : 'The read failed';
// A refusal fails closed: no kept data, the code in mono. Any other failure keeps the last read, labelled with its time.
const refusal = err => err.status === 403 || ['not-configured', 'no-bus-host'].includes(err.code);
- const failedView = err => `${head(failTitle(err))}<div class="banner ${refusal(err) ? 'ref' : 'err'}" role="alert"><p>${refusal(err) ? `<b>refused: <code>${txt(err.code)}</code>.</b> ` : ''}${why(err)} Console shows nothing rather than a guess. Nothing was written.</p>${retry}</div>${empty('Nothing to show.', refusal(err) ? 'The view stays empty until the problem above is fixed.' : 'Read again, or come back when the bus answers.')}`;
+ const failedView = err => `${head(failTitle(err))}<div class="banner ${refusal(err) ? 'ref' : 'err'}" role="alert"><p>${refusal(err) ? `<b>refused: ${txt(err.code)}.</b> ` : ''}${why(err)} Console shows nothing rather than a guess. Nothing was written.</p>${retry}</div>${empty('Nothing to show.', refusal(err) ? 'The view stays empty until the problem above is fixed.' : 'Read again, or come back when the bus answers.')}`;
const staleBanner = err => `<div class="banner err s1-stale" role="alert"><p><b>The last read failed.</b> ${why(err)} This is what was read before, ${when(readAt)}. Nothing was changed.</p>${retry}</div>`;
// Freshness line in the command bar for bus pages.
const stamp = iso => { const t = Date.parse(iso); return Number.isFinite(t) ? `${new Date(t).toISOString().slice(11, 19)} UTC` : 'unknown'; };
@@ -0,0 +1,36 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5248.350804ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6871.236761ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (9267.987367ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (970.658233ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (316.969409ms)
✔ bus refusals map to statuses and never carry a path or a stack (258.400443ms)
✔ humanCall speaks the human transport protocol without blocking the server (2432.255818ms)
✔ busReader takes --business, else the live bus host, else refuses (184.834293ms)
✔ humanCall kills a transport that runs past its timeout (528.843918ms)
✔ serve refuses a --business value that is not a business id (199.945476ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5325.287046ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3880.82363ms)
✔ conversation view: a newer session with no readable history keeps the marker (1896.347832ms)
✔ conversation view: seats without history say so and offer no reply (1341.397952ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7915.59178ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (57016.635343ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6078.323228ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23058.579464ms)
✔ loopback host and board origin fail closed (13.230744ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (310.414402ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (150.209364ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1760.353157ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.913986ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.340836ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (2.36321ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7850.747047ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2933.64496ms)
ℹ tests 27
ℹ suites 0
ℹ pass 27
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 57785.087003
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index f5389ee6..60995f29 100644
--- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js
@@ -375,7 +375,7 @@
const stamp = iso => { const t = Date.parse(iso); return Number.isFinite(t) ? `${new Date(t).toISOString().slice(11, 19)} UTC` : 'unknown'; };
function freshBus(state) {
$('fresh-bus').innerHTML = state === 'failed' ? 'Bus <span class="warn">not read: the read failed</span>'
- : `Bus read <b>${esc(stamp(readAt))}</b>${readAt ? ` (${esc(ago(readAt))})` : ''}${state === 'stale' ? ' · <span class="warn">stale: the last read failed</span>' : ''}`;
+ : `Bus read <b>${esc(stamp(readAt))}</b>${readAt ? ` (${esc(ago(readAt))})` : ''}`;
}
// ---------------------------------------------------------------- router
@@ -0,0 +1,71 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5791.84315ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5931.424696ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8208.335483ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (288.745639ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (95.589345ms)
✔ bus refusals map to statuses and never carry a path or a stack (43.548131ms)
✔ humanCall speaks the human transport protocol without blocking the server (3157.970328ms)
✔ busReader takes --business, else the live bus host, else refuses (182.605399ms)
✔ humanCall kills a transport that runs past its timeout (528.453388ms)
✔ serve refuses a --business value that is not a business id (173.453275ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5913.007381ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2826.523101ms)
✔ conversation view: a newer session with no readable history keeps the marker (1818.54918ms)
✔ conversation view: seats without history say so and offer no reply (1217.464307ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5971.136144ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55587.199523ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5785.97143ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22792.773591ms)
✔ loopback host and board origin fail closed (7.422204ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (114.173784ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (46.336575ms)
✔ unreachable board reports URL; CLI rejects unsupported options (921.432069ms)
✔ tokens.css and icons.svg are byte copies of docs/design (8.890882ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.17736ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.56056ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5850.038936ms)
✖ five states on Board, Inbox, Tasks, Agents and Trail (2552.965234ms)
ℹ tests 27
ℹ suites 0
ℹ pass 25
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 55919.285318
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5850.038936ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /stale: the last read failed/. Input:
'Bus read 17:41:19 UTC (0 s ago)'
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:187:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'Bus read 17:41:19 UTC (0 s ago)',
expected: /stale: the last read failed/,
operator: 'match',
diff: 'simple'
}
test at tests/shell.test.mjs:278:1
✖ five states on Board, Inbox, Tasks, Agents and Trail (2552.965234ms)
AssertionError [ERR_ASSERTION]: #/inbox
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:333:14)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 'Bus read 17:41:23 UTC (0 s ago)',
expected: /stale: the last read failed/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/app.js b/packages/webui/src/public/app.js
index 1bd88cf2..78a1bf8d 100644
--- a/packages/webui/src/public/app.js
+++ b/packages/webui/src/public/app.js
@@ -41,7 +41,7 @@
palette = p.id;
if (!['system', 'light', 'dim', 'dark'].includes(mode)) mode = 'system';
document.documentElement.dataset.palette = palette;
- if (mode === 'system') delete document.documentElement.dataset.mode; else document.documentElement.dataset.mode = mode;
+ document.documentElement.dataset.mode = mode;
$('palette').value = palette; $('mode').value = mode;
try { localStorage.setItem('mosaic-console-appearance', JSON.stringify({ palette, mode })); } catch {}
}
@@ -0,0 +1,56 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5268.206473ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5667.727448ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6423.223294ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (397.075731ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (126.647007ms)
✔ bus refusals map to statuses and never carry a path or a stack (50.303543ms)
✔ humanCall speaks the human transport protocol without blocking the server (2065.282756ms)
✔ busReader takes --business, else the live bus host, else refuses (206.772468ms)
✔ humanCall kills a transport that runs past its timeout (536.593624ms)
✔ serve refuses a --business value that is not a business id (216.440541ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5279.816257ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2768.013147ms)
✔ conversation view: a newer session with no readable history keeps the marker (1710.014915ms)
✔ conversation view: seats without history say so and offer no reply (955.788723ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5054.287458ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54831.221513ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4747.666103ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23326.297774ms)
✔ loopback host and board origin fail closed (9.530125ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (132.775432ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (129.121734ms)
✔ unreachable board reports URL; CLI rejects unsupported options (813.942492ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.744558ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.122253ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.750309ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5320.290317ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2836.644907ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 55294.850895
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5320.290317ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
true !== false
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:104:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: true,
expected: false,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index f5389ee6..e7305cbf 100644
--- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js
@@ -477,7 +477,7 @@
const t = $('toast'); clearTimeout(toastTimer);
if (text === null) { t.className = 'toast'; t.textContent = ''; return; }
t.className = `toast on${error ? ' err' : ''}`;
- t.innerHTML = `<span>${esc(text)}</span>${error ? '<button type="button" data-dismiss>Dismiss</button>' : ''}`;
+ t.innerHTML = `<span>${text}</span>${error ? '<button type="button" data-dismiss>Dismiss</button>' : ''}`;
if (!error) toastTimer = setTimeout(() => toast(null), 2600);
}
@@ -0,0 +1,36 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3498.424498ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5073.27053ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5860.251528ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (316.037593ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (106.598219ms)
✔ bus refusals map to statuses and never carry a path or a stack (85.782079ms)
✔ humanCall speaks the human transport protocol without blocking the server (2200.847869ms)
✔ busReader takes --business, else the live bus host, else refuses (239.201748ms)
✔ humanCall kills a transport that runs past its timeout (533.279441ms)
✔ serve refuses a --business value that is not a business id (256.027601ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3551.87569ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2463.641056ms)
✔ conversation view: a newer session with no readable history keeps the marker (1169.125934ms)
✔ conversation view: seats without history say so and offer no reply (841.998568ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3608.632384ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54356.685952ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3674.33323ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22885.872246ms)
✔ loopback host and board origin fail closed (9.397593ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (128.255402ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (105.743453ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1209.792727ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.322274ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.723359ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.811832ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4370.26785ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2285.134171ms)
ℹ tests 27
ℹ suites 0
ℹ pass 27
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 54819.589769
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index f5389ee6..8911ba97 100644
--- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js
@@ -496,7 +496,7 @@
const q = $('cmdk-input').value.trim().toLowerCase();
pkShown = pkAll.filter(i => !q || `${i.label} ${i.group}`.toLowerCase().includes(q)).slice(0, 50);
pkAt = Math.min(pkAt, Math.max(0, pkShown.length - 1));
- $('cmdk-list').innerHTML = pkShown.length ? pkShown.map((i, n) => `<li role="option" id="cmdk-opt-${n}" data-href="${esc(i.href)}" aria-selected="${n === pkAt}"><span>${txt(i.label)}</span><span class="g">${txt(i.group)}</span></li>`).join('')
+ $('cmdk-list').innerHTML = pkShown.length ? pkShown.map((i, n) => `<li role="option" id="cmdk-opt-${n}" data-href="${esc(i.href)}" aria-selected="${n === pkAt}"><span>${i.label}</span><span class="g">${txt(i.group)}</span></li>`).join('')
: '<li class="pk-none" role="option" aria-disabled="true" aria-selected="false">No view, decision or task matches.</li>';
if (pkShown.length) { $('cmdk-input').setAttribute('aria-activedescendant', `cmdk-opt-${pkAt}`); $(`cmdk-opt-${pkAt}`)?.scrollIntoView({ block: 'nearest' }); }
else $('cmdk-input').removeAttribute('aria-activedescendant');
@@ -0,0 +1,36 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5754.334404ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6557.501052ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8416.074858ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (574.930974ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (797.089799ms)
✔ bus refusals map to statuses and never carry a path or a stack (302.286778ms)
✔ humanCall speaks the human transport protocol without blocking the server (1995.597872ms)
✔ busReader takes --business, else the live bus host, else refuses (228.695172ms)
✔ humanCall kills a transport that runs past its timeout (528.891783ms)
✔ serve refuses a --business value that is not a business id (167.030411ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5671.033678ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3129.076355ms)
✔ conversation view: a newer session with no readable history keeps the marker (1801.429431ms)
✔ conversation view: seats without history say so and offer no reply (899.175464ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6209.788072ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55273.916567ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6379.070681ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24135.002505ms)
✔ loopback host and board origin fail closed (9.440587ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (152.276494ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (124.824653ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1771.062865ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.492243ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.818143ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.856825ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6039.027572ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3366.746341ms)
ℹ tests 27
ℹ suites 0
ℹ pass 27
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 55726.493145
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
index ae98ebd5..1d0049e5 100644
--- a/packages/webui/src/public/shell.css
+++ b/packages/webui/src/public/shell.css
@@ -18,7 +18,7 @@ body{font:400 14px/1.5 var(--font);color:var(--text);background:var(--canvas)}
code,kbd,pre,.mono{font-family:var(--mono);font-size:12.5px}
kbd{border:1px solid var(--line);border-bottom-width:2px;border-radius:4px;padding:0 .35em;background:var(--surface);color:var(--strong);white-space:nowrap;font-size:11.5px}
.count,td,.s1-twhen,time,.fresh,.kv dd{font-variant-numeric:tabular-nums}
-:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
+:focus-visible{outline:2px solid var(--focus);outline-offset:0}
table.sessions tbody tr:focus-visible,table.s1-table tbody tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
svg.i{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
@@ -0,0 +1,36 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4400.430089ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4300.640965ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5301.127984ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (276.547118ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (103.39068ms)
✔ bus refusals map to statuses and never carry a path or a stack (65.485599ms)
✔ humanCall speaks the human transport protocol without blocking the server (2151.965579ms)
✔ busReader takes --business, else the live bus host, else refuses (163.976348ms)
✔ humanCall kills a transport that runs past its timeout (528.285651ms)
✔ serve refuses a --business value that is not a business id (172.181607ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4371.961867ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2840.647816ms)
✔ conversation view: a newer session with no readable history keeps the marker (1998.086292ms)
✔ conversation view: seats without history say so and offer no reply (1131.857592ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4574.654406ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53632.384811ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4727.108522ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22586.932856ms)
✔ loopback host and board origin fail closed (7.712823ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (138.907671ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (128.085616ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1065.673871ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.242354ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.442883ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.419635ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4421.026726ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2740.769195ms)
ℹ tests 27
ℹ suites 0
ℹ pass 27
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 54058.53995
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
index ae98ebd5..a410b866 100644
--- a/packages/webui/src/public/shell.css
+++ b/packages/webui/src/public/shell.css
@@ -18,7 +18,7 @@ body{font:400 14px/1.5 var(--font);color:var(--text);background:var(--canvas)}
code,kbd,pre,.mono{font-family:var(--mono);font-size:12.5px}
kbd{border:1px solid var(--line);border-bottom-width:2px;border-radius:4px;padding:0 .35em;background:var(--surface);color:var(--strong);white-space:nowrap;font-size:11.5px}
.count,td,.s1-twhen,time,.fresh,.kv dd{font-variant-numeric:tabular-nums}
-:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
+:focus-visible{outline:1px solid var(--focus);outline-offset:2px}
table.sessions tbody tr:focus-visible,table.s1-table tbody tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
svg.i{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
@@ -0,0 +1,53 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3859.095965ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4343.268551ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6605.907754ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (526.606027ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (153.465165ms)
✔ bus refusals map to statuses and never carry a path or a stack (55.167495ms)
✔ humanCall speaks the human transport protocol without blocking the server (2288.47965ms)
✔ busReader takes --business, else the live bus host, else refuses (261.219118ms)
✔ humanCall kills a transport that runs past its timeout (531.076935ms)
✔ serve refuses a --business value that is not a business id (384.431269ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3856.193842ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3060.373427ms)
✔ conversation view: a newer session with no readable history keeps the marker (1167.486824ms)
✔ conversation view: seats without history say so and offer no reply (944.374404ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3875.563993ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56587.254808ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3876.724799ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23324.969843ms)
✔ loopback host and board origin fail closed (8.786574ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (146.934201ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (134.739961ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1346.776736ms)
✔ tokens.css and icons.svg are byte copies of docs/design (0.958173ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.776603ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.395916ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (3843.314767ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2988.092ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 57019.428623
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (3843.314767ms)
AssertionError [ERR_ASSERTION]: #cmdk outline 1px
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:163:69)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
index ae98ebd5..473b94fe 100644
--- a/packages/webui/src/public/shell.css
+++ b/packages/webui/src/public/shell.css
@@ -165,7 +165,7 @@ body.on-bus #board-refused{display:none}
.cmd-right label{flex:1 1 0;min-width:0}
.cmd-right .btn{flex:none}
/* The section list wraps rather than scrolling, so a blocking count never hides a section. */
- .s1-sections{display:flex;flex-wrap:wrap;overflow-x:visible;flex-basis:100%;gap:2px;padding-bottom:2px}
+ .s1-sections{display:flex;flex-basis:100%;gap:2px;padding-bottom:2px}
.s1-section{border-radius:var(--r);padding:5px 8px;white-space:nowrap;flex:none}
.inspector{inset:0;top:0;width:100%;z-index:40;box-shadow:none}
}
@@ -0,0 +1,36 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (7740.004399ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (15045.20497ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (15277.238224ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (1915.102997ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (37.390122ms)
✔ bus refusals map to statuses and never carry a path or a stack (16.731727ms)
✔ humanCall speaks the human transport protocol without blocking the server (1654.594624ms)
✔ busReader takes --business, else the live bus host, else refuses (229.623851ms)
✔ humanCall kills a transport that runs past its timeout (528.322018ms)
✔ serve refuses a --business value that is not a business id (173.324632ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (11063.547959ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4337.313117ms)
✔ conversation view: a newer session with no readable history keeps the marker (1973.678474ms)
✔ conversation view: seats without history say so and offer no reply (1440.938589ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (8131.126692ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (51061.883178ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (7907.667489ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (28815.695414ms)
✔ loopback host and board origin fail closed (8.80561ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (302.179616ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (203.376087ms)
✔ unreachable board reports URL; CLI rejects unsupported options (867.064023ms)
✔ tokens.css and icons.svg are byte copies of docs/design (0.895645ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.910895ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.387285ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8972.170949ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (6074.516516ms)
ℹ tests 27
ℹ suites 0
ℹ pass 27
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 51415.549688
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index f5389ee6..7dc2fb2c 100644
--- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js
@@ -209,7 +209,7 @@
const items = [['/', 'Board', '', 'board'], ['/inbox', 'Inbox', inboxCount ? `<span class="count" aria-label="${inboxCount.open} open for you">${inboxCount.open}</span>${inboxCount.blocking ? ` ${blockChip(`${inboxCount.blocking} blocking`)}` : ''}` : '', 'inbox'], ['/tasks', 'Tasks', '', 'tasks'], ['/agents', 'Agents', '', 'agents']];
$('sections').innerHTML = items.map(([h, l, extra, icon]) => {
const cur = h === '/' ? route === '/' : route.startsWith(h) || (h === '/tasks' && route.startsWith('/trail/task')) || (h === '/inbox' && route.startsWith('/trail/decision'));
- return `<li><a href="#${h}" class="s1-section"${cur ? ' aria-current="page"' : ''}><svg class="i" aria-hidden="true"><use href="/icons.svg#i-${icon}"/></svg>${l}${extra ? ` ${extra}` : ''}</a></li>`;
+ return `<li><a href="#${h}" class="s1-section"${cur ? ' aria-current="page"' : ''}>${l}${extra ? ` ${extra}` : ''}</a></li>`;
}).join('');
}
function countInbox(rows) { inboxCount = { open: rows.length, blocking: rows.filter(d => d.blocking).length }; }
@@ -0,0 +1,56 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (9498.845618ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (14178.704126ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (14655.718816ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (1567.119707ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (44.104039ms)
✔ bus refusals map to statuses and never carry a path or a stack (20.542495ms)
✔ humanCall speaks the human transport protocol without blocking the server (1704.198859ms)
✔ busReader takes --business, else the live bus host, else refuses (193.649625ms)
✔ humanCall kills a transport that runs past its timeout (529.634379ms)
✔ serve refuses a --business value that is not a business id (195.352905ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (13040.964383ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3460.713697ms)
✔ conversation view: a newer session with no readable history keeps the marker (1590.068006ms)
✔ conversation view: seats without history say so and offer no reply (1041.528275ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7155.666046ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (61163.295912ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (8768.9756ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (30593.233514ms)
✔ loopback host and board origin fail closed (10.042054ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (317.593824ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (158.705182ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1128.252253ms)
✔ tokens.css and icons.svg are byte copies of docs/design (0.836867ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.649855ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.204314ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7835.16493ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (5311.153032ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 61560.528856
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7835.16493ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
0 !== 4
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:174:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 0,
expected: 4,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,12 @@
diff --git a/packages/webui/src/public/app.js b/packages/webui/src/public/app.js
index 1bd88cf2..05a6089e 100644
--- a/packages/webui/src/public/app.js
+++ b/packages/webui/src/public/app.js
@@ -296,7 +296,6 @@
function accept(value) {
if (!value || !Array.isArray(value.sessions) || value.sessions.some(r => !r || typeof r.project !== 'string' || typeof r.agent !== 'string')) throw new Error('Invalid board session data');
data = value; failed = false; $('error').hidden = true; $('error').className = 'banner err';
- $('board-refused').hidden = true; document.body.classList.remove('board-refused');
for (const id of awaiting.keys()) { const r = row(id); if (r && !pending(r)) awaiting.delete(id); }
render();
}
@@ -0,0 +1,55 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (7188.855809ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7271.704177ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8077.985604ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (532.536088ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (239.548482ms)
✔ bus refusals map to statuses and never carry a path or a stack (25.479189ms)
✔ humanCall speaks the human transport protocol without blocking the server (2707.003302ms)
✔ busReader takes --business, else the live bus host, else refuses (368.432283ms)
✔ humanCall kills a transport that runs past its timeout (532.75841ms)
✔ serve refuses a --business value that is not a business id (253.697624ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7156.651219ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2332.626421ms)
✔ conversation view: a newer session with no readable history keeps the marker (1564.440958ms)
✔ conversation view: seats without history say so and offer no reply (1088.29525ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7330.458092ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (57918.906247ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (7193.459074ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24552.102054ms)
✔ loopback host and board origin fail closed (9.968967ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (200.59317ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (312.379414ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1496.667453ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.404264ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.784097ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.975862ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7313.200709ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2852.110982ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 58512.874055
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7313.200709ms)
AssertionError [ERR_ASSERTION]: Expected "actual" to be strictly unequal to:
'none'
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:147:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'none',
expected: 'none',
operator: 'notStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index f5389ee6..24750eb1 100644
--- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js
@@ -525,7 +525,7 @@
const a = e.target.closest?.('#bus-view table.s1-table td.s1-title a');
if (!a || (e.key !== 'ArrowDown' && e.key !== 'ArrowUp')) return;
const links = [...a.closest('table').querySelectorAll('td.s1-title a')], i = links.indexOf(a);
- e.preventDefault(); links[i + (e.key === 'ArrowDown' ? 1 : -1)]?.focus();
+ e.preventDefault(); links[i + (e.key === 'ArrowDown' ? -1 : 1)]?.focus();
});
window.addEventListener('hashchange', () => render(true));
$('refresh').addEventListener('click', () => render(false));
@@ -0,0 +1,56 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (7435.806805ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7593.581724ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7674.576199ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (288.355604ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (177.319585ms)
✔ bus refusals map to statuses and never carry a path or a stack (56.443497ms)
✔ humanCall speaks the human transport protocol without blocking the server (2392.104767ms)
✔ busReader takes --business, else the live bus host, else refuses (283.865828ms)
✔ humanCall kills a transport that runs past its timeout (530.708861ms)
✔ serve refuses a --business value that is not a business id (293.569333ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7220.28677ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2322.290614ms)
✔ conversation view: a newer session with no readable history keeps the marker (1306.780472ms)
✔ conversation view: seats without history say so and offer no reply (977.03291ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7074.876596ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56183.087593ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6068.013766ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (25233.057689ms)
✔ loopback host and board origin fail closed (9.354686ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (147.853081ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (132.817076ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1234.273937ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.454396ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.999574ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.794182ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7048.453702ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2437.068864ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 56798.36445
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7048.453702ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /Second task/. Input:
'#7 Kept task'
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:190:38)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '#7 Kept task',
expected: /Second task/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/serve.mjs b/packages/webui/src/serve.mjs
index 280a228f..93a1958f 100644
--- a/packages/webui/src/serve.mjs
+++ b/packages/webui/src/serve.mjs
@@ -22,7 +22,7 @@ const files = new Map([
['/', ['index.html', 'text/html; charset=utf-8']],
...['app.js', 'brand.js', 'bus.js'].map(f => ['/' + f, [f, 'text/javascript; charset=utf-8']]),
...['shared/app.css', 'tokens.css', 'console.css', 'live.css', 'bus.css', 'shell.css'].map(f => ['/' + f, [f, 'text/css; charset=utf-8']]),
- ['/icons.svg', ['icons.svg', 'image/svg+xml']],
+ ['/icons.svg', ['icons.svg', 'text/plain']],
...[400, 500, 600, 700].map(w => [`/assets/fonts/manrope-${w}.woff2`, [`assets/fonts/manrope-${w}.woff2`, 'font/woff2']]),
...[400, 500, 700].map(w => [`/assets/fonts/jetbrains-mono-${w}.woff2`, [`assets/fonts/jetbrains-mono-${w}.woff2`, 'font/woff2']]),
]);
@@ -0,0 +1,53 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5198.663054ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5584.343092ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7086.448634ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (267.907334ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (134.094408ms)
✔ bus refusals map to statuses and never carry a path or a stack (114.691386ms)
✔ humanCall speaks the human transport protocol without blocking the server (2562.538599ms)
✔ busReader takes --business, else the live bus host, else refuses (332.854133ms)
✔ humanCall kills a transport that runs past its timeout (537.451045ms)
✔ serve refuses a --business value that is not a business id (322.845795ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5397.416668ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2651.201165ms)
✔ conversation view: a newer session with no readable history keeps the marker (1371.821427ms)
✔ conversation view: seats without history say so and offer no reply (1038.855758ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5467.101437ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54944.465486ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5191.271543ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24030.373521ms)
✔ loopback host and board origin fail closed (11.434928ms)
✖ real board fixture passes through WebUI; assets and isolated seen/reply work (119.713354ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (86.02832ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1407.454964ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.624361ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.168129ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.840078ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5147.231405ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2781.962804ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 55451.838299
✖ failing tests:
test at tests/serve.test.mjs:22:1
✖ real board fixture passes through WebUI; assets and isolated seen/reply work (119.713354ms)
AssertionError [ERR_ASSERTION]: /icons.svg
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/serve.test.mjs:41:101)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
index ae98ebd5..ef259cfa 100644
--- a/packages/webui/src/public/shell.css
+++ b/packages/webui/src/public/shell.css
@@ -17,7 +17,7 @@ body{font:400 14px/1.5 var(--font);color:var(--text);background:var(--canvas)}
.tree-title,.label{font:700 11px/1.3 var(--font);letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
code,kbd,pre,.mono{font-family:var(--mono);font-size:12.5px}
kbd{border:1px solid var(--line);border-bottom-width:2px;border-radius:4px;padding:0 .35em;background:var(--surface);color:var(--strong);white-space:nowrap;font-size:11.5px}
-.count,td,.s1-twhen,time,.fresh,.kv dd{font-variant-numeric:tabular-nums}
+
:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
table.sessions tbody tr:focus-visible,table.s1-table tbody tr:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
svg.i{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
@@ -0,0 +1,36 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5815.739101ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6306.605756ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6556.920755ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (230.898299ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (124.363066ms)
✔ bus refusals map to statuses and never carry a path or a stack (99.788074ms)
✔ humanCall speaks the human transport protocol without blocking the server (2290.268607ms)
✔ busReader takes --business, else the live bus host, else refuses (512.295669ms)
✔ humanCall kills a transport that runs past its timeout (532.334409ms)
✔ serve refuses a --business value that is not a business id (378.3221ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5862.379595ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2207.341974ms)
✔ conversation view: a newer session with no readable history keeps the marker (1319.855887ms)
✔ conversation view: seats without history say so and offer no reply (945.392215ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5845.529279ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55253.928142ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5750.276641ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23895.725251ms)
✔ loopback host and board origin fail closed (11.74274ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (138.324612ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (125.990286ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1148.873598ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.610914ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.83ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (2.108441ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6080.959306ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2551.546835ms)
ℹ tests 27
ℹ suites 0
ℹ pass 27
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 55753.3057
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/app.js b/packages/webui/src/public/app.js
index 1bd88cf2..889f9b50 100644
--- a/packages/webui/src/public/app.js
+++ b/packages/webui/src/public/app.js
@@ -43,7 +43,7 @@
document.documentElement.dataset.palette = palette;
if (mode === 'system') delete document.documentElement.dataset.mode; else document.documentElement.dataset.mode = mode;
$('palette').value = palette; $('mode').value = mode;
- try { localStorage.setItem('mosaic-console-appearance', JSON.stringify({ palette, mode })); } catch {}
+ try { localStorage.setItem('mosaic-console-look', JSON.stringify({ palette, mode })); } catch {}
}
$('palette').innerHTML = Brand.palettes.map(p => `<option value="${p.id}">${p.name}</option>`).join('');
try { const saved = JSON.parse(localStorage.getItem('mosaic-console-appearance')); if (saved) { palette = saved.palette; mode = saved.mode; } } catch {}
@@ -0,0 +1,48 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (6509.180091ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6694.384219ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8448.50645ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (283.095687ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (94.261689ms)
✔ bus refusals map to statuses and never carry a path or a stack (171.22577ms)
✔ humanCall speaks the human transport protocol without blocking the server (3166.486182ms)
✔ busReader takes --business, else the live bus host, else refuses (634.294634ms)
✔ humanCall kills a transport that runs past its timeout (546.416392ms)
✔ serve refuses a --business value that is not a business id (277.920278ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5843.479393ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2979.713955ms)
✔ conversation view: a newer session with no readable history keeps the marker (1525.00909ms)
✔ conversation view: seats without history say so and offer no reply (1119.901705ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5907.046805ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56798.491522ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5982.963587ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23060.663865ms)
✔ loopback host and board origin fail closed (9.911459ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (151.827716ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (120.177257ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1888.294464ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.461055ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.105203ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.844539ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5599.305116ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3190.605625ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 57257.534522
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5599.305116ms)
Error: Uncaught: TypeError: Cannot read properties of null (reading 'mode')
at <anonymous>:1:62
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/browser.mjs:44:35)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:117:18)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
index ae98ebd5..618e1126 100644
--- a/packages/webui/src/public/shell.css
+++ b/packages/webui/src/public/shell.css
@@ -97,7 +97,7 @@ table.s1-table td code{font-size:12.5px;white-space:nowrap}
.skel{display:block;height:12px;border-radius:4px;background:var(--raised);animation:skel 1.4s ease-in-out infinite}
.skel:nth-child(odd){width:82%}
@keyframes skel{50%{opacity:.45}}
-body.board-refused #board-view{display:none}
+
body.on-bus #board-refused{display:none}
/* Toast: one line, polite, bottom centre. */
@@ -0,0 +1,56 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4795.15073ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5559.593964ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7776.997828ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (1077.378774ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (172.941475ms)
✔ bus refusals map to statuses and never carry a path or a stack (171.347229ms)
✔ humanCall speaks the human transport protocol without blocking the server (2180.586418ms)
✔ busReader takes --business, else the live bus host, else refuses (290.631709ms)
✔ humanCall kills a transport that runs past its timeout (528.862733ms)
✔ serve refuses a --business value that is not a business id (172.199777ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5528.326861ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3194.267668ms)
✔ conversation view: a newer session with no readable history keeps the marker (1843.504674ms)
✔ conversation view: seats without history say so and offer no reply (1433.153552ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6538.626521ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55681.83814ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6466.894866ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22981.556096ms)
✔ loopback host and board origin fail closed (10.31224ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (151.493703ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (131.02258ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1643.650438ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.118919ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.164409ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.524389ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4961.827843ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3334.087324ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 56099.695862
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4961.827843ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
'block' !== 'none'
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:142:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'block',
expected: 'none',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
index f5389ee6..c40651fd 100644
--- a/packages/webui/src/public/bus.js
+++ b/packages/webui/src/public/bus.js
@@ -430,7 +430,7 @@
try { html = await view(live, path, new URLSearchParams(query)); readAt = live.oldest(); }
catch (err) {
const kept = reader(false);
- try { if (refusal(err)) throw err; html = await view(kept, path, new URLSearchParams(query)); readAt = kept.oldest(); html = staleBanner(err) + html; state = 'stale'; }
+ try { html = await view(kept, path, new URLSearchParams(query)); readAt = kept.oldest(); html = staleBanner(err) + html; state = 'stale'; }
catch { html = failedView(err); state = 'failed'; }
}
if (g !== gen) return;
@@ -0,0 +1,56 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5189.150978ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4797.468611ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7333.317174ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (293.313629ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (270.398019ms)
✔ bus refusals map to statuses and never carry a path or a stack (46.997252ms)
✔ humanCall speaks the human transport protocol without blocking the server (2498.469401ms)
✔ busReader takes --business, else the live bus host, else refuses (561.937736ms)
✔ humanCall kills a transport that runs past its timeout (529.448403ms)
✔ serve refuses a --business value that is not a business id (158.123778ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5165.831607ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2318.690956ms)
✔ conversation view: a newer session with no readable history keeps the marker (1214.295855ms)
✔ conversation view: seats without history say so and offer no reply (858.950234ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5772.38394ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56013.500976ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5767.671969ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22044.47108ms)
✔ loopback host and board origin fail closed (8.098491ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (116.266396ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (149.164044ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1526.468607ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.016667ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.453188ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.576672ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (10536.386855ms)
✖ five states on Board, Inbox, Tasks, Agents and Trail (6210.0518ms)
ℹ tests 27
ℹ suites 0
ℹ pass 25
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 56442.086324
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (10536.386855ms)
Error: Uncaught (in promise) Error: Condition timed out: !!document.querySelector("#bus-view .banner.ref"): undefined
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/browser.mjs:44:35)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:196:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
test at tests/shell.test.mjs:278:1
✖ five states on Board, Inbox, Tasks, Agents and Trail (6210.0518ms)
Error: Uncaught (in promise) Error: Condition timed out: !!document.querySelector("#bus-view .banner.ref"): undefined
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/browser.mjs:44:35)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:337:7)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
@@ -0,0 +1,13 @@
diff --git a/packages/webui/src/public/app.js b/packages/webui/src/public/app.js
index 1bd88cf2..560b6b11 100644
--- a/packages/webui/src/public/app.js
+++ b/packages/webui/src/public/app.js
@@ -304,7 +304,7 @@
function error(err) {
failed = true; $('error').hidden = false;
const refused = err.status === 403;
- $('error').className = refused ? 'banner ref' : 'banner err';
+ $('error').className = 'banner err';
$('board-refused').hidden = !refused; document.body.classList.toggle('board-refused', refused);
$('error').innerHTML = refused
? `<p><b>The board refused the read.</b> refused: <code>${esc(err.message)}</code> Board: ${esc(board)}. Console shows nothing rather than a guess. Use Refresh to try again.</p>`
@@ -0,0 +1,59 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4106.705441ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4897.321923ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7067.560106ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (240.258586ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (133.598704ms)
✔ bus refusals map to statuses and never carry a path or a stack (67.321441ms)
✔ humanCall speaks the human transport protocol without blocking the server (3115.331024ms)
✔ busReader takes --business, else the live bus host, else refuses (181.812826ms)
✔ humanCall kills a transport that runs past its timeout (527.655765ms)
✔ serve refuses a --business value that is not a business id (166.522998ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4231.728036ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2973.78925ms)
✔ conversation view: a newer session with no readable history keeps the marker (1229.737715ms)
✔ conversation view: seats without history say so and offer no reply (730.936985ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4274.78806ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55233.84178ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4670.272359ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22574.934461ms)
✔ loopback host and board origin fail closed (7.615952ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (131.841633ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (99.940777ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1879.694356ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.434754ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.048983ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (1.308296ms)
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4582.403832ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3147.731873ms)
ℹ tests 27
ℹ suites 0
ℹ pass 26
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 55736.314772
✖ failing tests:
test at tests/shell.test.mjs:84:1
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4582.403832ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ 'banner err'
- 'banner ref'
^
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53a/mutwt/packages/webui/tests/shell.test.mjs:140:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'banner err',
expected: 'banner ref',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,16 @@
packages/webui/README.md: OK
packages/webui/src/public/app.js: OK
packages/webui/src/public/assets/fonts/jetbrains-mono-400.woff2: OK
packages/webui/src/public/assets/fonts/jetbrains-mono-500.woff2: OK
packages/webui/src/public/assets/fonts/jetbrains-mono-700.woff2: OK
packages/webui/src/public/assets/fonts/jetbrains-mono-OFL.txt: OK
packages/webui/src/public/assets/fonts/jetbrains-mono-sources.txt: OK
packages/webui/src/public/bus.js: OK
packages/webui/src/public/icons.svg: OK
packages/webui/src/public/index.html: OK
packages/webui/src/public/shell.css: OK
packages/webui/src/public/tokens.css: OK
packages/webui/src/serve.mjs: OK
packages/webui/tests/browser.mjs: OK
packages/webui/tests/serve.test.mjs: OK
packages/webui/tests/shell.test.mjs: OK
@@ -0,0 +1,31 @@
# mutate.py <id>: apply one named mutant to the candidate (exact text, must match once). Run from the tree root.
import sys
B, A, C, S, H = "packages/webui/src/public/bus.js", "packages/webui/src/public/app.js", "packages/webui/src/public/shell.css", "packages/webui/src/serve.mjs", "packages/webui/src/public/index.html"
M = {
"M01": [(B, "err.status === 403 || ['not-configured', 'no-bus-host'].includes(err.code);", "err.status === 403;")],
"M02": [(A, "const refused = err.status === 403;", "const refused = false;")],
"M03": [(A, "document.body.classList.toggle('board-refused', refused);", "")],
"M04": [(B, "<b>refused: <code>${txt(err.code)}</code>.</b> ", "<b>refused: ${txt(err.code)}.</b> ")],
"M05": [(B, "${state === 'stale' ? ' · <span class=\"warn\">stale: the last read failed</span>' : ''}", "")],
"M06": [(A, "if (mode === 'system') delete document.documentElement.dataset.mode; else document.documentElement.dataset.mode = mode;", "document.documentElement.dataset.mode = mode;")],
"M07": [(B, "t.innerHTML = `<span>${esc(text)}</span>", "t.innerHTML = `<span>${text}</span>")],
"M08": [(B, "<span>${txt(i.label)}</span><span class=\"g\">", "<span>${i.label}</span><span class=\"g\">")],
"M09": [(C, ":focus-visible{outline:2px solid var(--focus);outline-offset:2px}", ":focus-visible{outline:2px solid var(--focus);outline-offset:0}")],
"M10": [(C, ":focus-visible{outline:2px solid var(--focus);outline-offset:2px}", ":focus-visible{outline:1px solid var(--focus);outline-offset:2px}")],
"M11": [(C, " .s1-sections{display:flex;flex-wrap:wrap;overflow-x:visible;flex-basis:100%;gap:2px;padding-bottom:2px}", " .s1-sections{display:flex;flex-basis:100%;gap:2px;padding-bottom:2px}")],
"M12": [(B, "<svg class=\"i\" aria-hidden=\"true\"><use href=\"/icons.svg#i-${icon}\"/></svg>", "")],
"M13": [(A, " $('board-refused').hidden = true; document.body.classList.remove('board-refused');\n", "")],
"M14": [(B, "links[i + (e.key === 'ArrowDown' ? 1 : -1)]?.focus();", "links[i + (e.key === 'ArrowDown' ? -1 : 1)]?.focus();")],
"M15": [(S, "['/icons.svg', ['icons.svg', 'image/svg+xml']],", "['/icons.svg', ['icons.svg', 'text/plain']],")],
"M16": [(C, ".count,td,.s1-twhen,time,.fresh,.kv dd{font-variant-numeric:tabular-nums}", "")],
"M17": [(A, "try { localStorage.setItem('mosaic-console-appearance',", "try { localStorage.setItem('mosaic-console-look',")],
"M18": [(C, "body.board-refused #board-view{display:none}", "")],
"M19": [(B, "if (refusal(err)) throw err; html = await view(kept,", "html = await view(kept,")],
"M20": [(A, "$('error').className = refused ? 'banner ref' : 'banner err';", "$('error').className = 'banner err';")],
}
for f, old, new in M[sys.argv[1]]:
s = open(f).read()
n = s.count(old)
if n != 1: sys.exit(f"{sys.argv[1]}: {n} matches in {f}")
open(f, "w").write(s.replace(old, new))
print(sys.argv[1], "applied")
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# Each mutant against the packages/webui suite in a candidate worktree, then restores from the index.
export TMPDIR=~/darkwing-scratch/r53a/tmp
S=~/darkwing-scratch/r53a; M=$S/mut; W=$S/mutwt
: > $M/summary.txt
echo "start $(date -u +%FT%TZ)" >> $M/summary.txt
for v in $(python3 -c 'import re;print(" ".join(sorted(set(re.findall(r"\"(M\d\d)\"", open("'$M'/mutate.py").read())))))'); do
(cd $W && git checkout -q -- packages/webui && python3 $M/mutate.py $v > /dev/null && git diff > $M/$v.diff) || { echo "$v APPLY-FAILED" >> $M/summary.txt; continue; }
(cd $W/packages/webui && env -u NODE_TEST_CONTEXT node --test 'tests/*.test.mjs' > $M/$v.txt 2>&1); e=$?
r=killed; [ $e = 0 ] && r=SURVIVED
echo "$v $r exit=$e $(grep -E '^ℹ (pass|fail)' $M/$v.txt | tr '\n' ' ')" >> $M/summary.txt
done
(cd $W && git checkout -q -- packages/webui && sha256sum -c $S/pkt/candidate-manifest.sha256) > $M/manifest-after.txt 2>&1
echo "end $(date -u +%FT%TZ)" >> $M/summary.txt
echo DONE >> $M/summary.txt
@@ -0,0 +1,23 @@
start 2026-10-10T17:37:26Z
M01 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
M02 killed exit=1 ℹ pass 26 ℹ fail 1
M03 killed exit=1 ℹ pass 26 ℹ fail 1
M04 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
M05 killed exit=1 ℹ pass 25 ℹ fail 2
M06 killed exit=1 ℹ pass 26 ℹ fail 1
M07 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
M08 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
M09 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
M10 killed exit=1 ℹ pass 26 ℹ fail 1
M11 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
M12 killed exit=1 ℹ pass 26 ℹ fail 1
M13 killed exit=1 ℹ pass 26 ℹ fail 1
M14 killed exit=1 ℹ pass 26 ℹ fail 1
M15 killed exit=1 ℹ pass 26 ℹ fail 1
M16 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
M17 killed exit=1 ℹ pass 26 ℹ fail 1
M18 killed exit=1 ℹ pass 26 ℹ fail 1
M19 killed exit=1 ℹ pass 25 ℹ fail 2
M20 killed exit=1 ℹ pass 26 ℹ fail 1
end 2026-10-10T17:56:15Z
DONE
@@ -0,0 +1 @@
tokens.css is current
@@ -0,0 +1,36 @@
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3254.037973ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4707.01393ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6741.095235ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (341.416961ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (180.268473ms)
✔ bus refusals map to statuses and never carry a path or a stack (112.024139ms)
✔ humanCall speaks the human transport protocol without blocking the server (2572.17701ms)
✔ busReader takes --business, else the live bus host, else refuses (222.163707ms)
✔ humanCall kills a transport that runs past its timeout (535.12865ms)
✔ serve refuses a --business value that is not a business id (197.661373ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3213.259707ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3818.250352ms)
✔ conversation view: a newer session with no readable history keeps the marker (1561.547729ms)
✔ conversation view: seats without history say so and offer no reply (1241.12093ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5596.096268ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55064.519411ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4872.400872ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23577.983738ms)
✔ loopback host and board origin fail closed (9.569663ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (138.151914ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (103.234277ms)
✔ unreachable board reports URL; CLI rejects unsupported options (1667.864763ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.458809ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.787806ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (2.003515ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4887.914731ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3288.045605ms)
ℹ tests 27
ℹ suites 0
ℹ pass 27
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 55485.658358
@@ -0,0 +1,14 @@
start 2026-10-10T17:31:49Z
node-webui exit=0 ℹ pass 27 ℹ fail 0
build-tokens --check exit=0 tokens.css is current
test-auth exit=0 selftest: 15 passed, 0 failed
test-conductor exit=0 selftest: 17 passed, 0 failed
test-config exit=0 selftest: 24 passed, 0 failed
test-discord exit=0 discord suite: 66 passed, 0 failed
test-extension-package exit=0 extension package selftest: 18 passed, 0 failed
test-foundation exit=0 selftest: 44 passed, 0 failed
test-queue exit=0 queue suite: 27 passed, 0 failed
test-release exit=0 selftest: 4 passed, 0 failed
test-task exit=0 selftest: 26 passed, 0 failed
end 2026-10-10T17:34:30Z
DONE
@@ -0,0 +1,17 @@
OK status with missing harness credential exits 3 and still lists accounts
OK status reports harness credential (read-only) + mosaic accounts
OK api key material never reaches output
OK oauth token material never reaches output
OK unparseable credential file exits 2
OK symlinked credential file exits 4
OK env-side credential names reported
OK env var values never reach output
OK accounts without an accounts dir reports none and creates nothing
OK accounts lists files and marks the active one
OK loose account perms flagged in listing
OK agent --auth with missing account file refuses (exit 4)
OK agent --auth with non-0600 account file refuses
OK agent --auth with invalid account name refuses
OK auth.sh without valid config refuses
selftest: 15 passed, 0 failed
@@ -0,0 +1,55 @@
Note: switching to '8a7871ff00f35f943054f5beb68b19f31ed1a3bd'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
Not currently on any branch.
nothing to commit, working tree clean
Note: switching to '8a7871ff00f35f943054f5beb68b19f31ed1a3bd'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
OK dry-run committed nothing
OK apply: allowed change exits 0 (exit 0)
OK apply: attribution in commit subject
OK apply: target tree clean after commit
OK disallowed path refused (exit 1)
OK disallowed path: target untouched
OK syntax gate refused broken .mjs (exit 1)
OK syntax gate: target untouched
OK suite failure refused (exit 1)
OK suite failure: target reverted to clean
OK disabled policy refused (exit 2)
OK disabled policy: target untouched
OK failed run refused (exit 1)
OK failed run: target untouched
OK missing run exits 4 (exit 4)
OK invalid policy exits 2 (exit 2)
selftest: 17 passed, 0 failed
@@ -0,0 +1,26 @@
OK absent adapter defaults to pi
OK adapter mock validates (exit 0)
OK unsupported adapter exits 2 (exit 2)
OK env exports adapter
OK bootstrap creates default when absent (exit 0)
OK bootstrap wrote config file
OK bootstrap is idempotent on existing config (exit 0)
OK bootstrap did not rewrite existing config
OK validate missing config exits 3 (exit 3)
OK malformed JSON exits 2 (exit 2)
OK unsupported configVersion exits 2 (exit 2)
OK unknown top-level key exits 2 (exit 2)
OK unknown execution key exits 2 (exit 2)
OK unsupported backend exits 2 (exit 2)
OK unsupported environment exits 2 (exit 2)
OK relative dataRoot exits 2 (exit 2)
OK non-canonical dataRoot exits 2 (exit 2)
OK filesystem root dataRoot exits 2 (exit 2)
OK home directory dataRoot exits 2 (exit 2)
OK dataRoot containing config dir exits 2 (exit 2)
OK control character in provider exits 2 (exit 2)
OK symlinked config file exits 2 (exit 2)
OK env exports resolve correctly
OK failed validation modified nothing
selftest: 24 passed, 0 failed
@@ -0,0 +1,70 @@
toolchain: node v26.8.1
OK syntax: packages/discord/src/approvals.mjs
OK syntax: packages/discord/src/authorize.mjs
OK syntax: packages/discord/src/binding.mjs
OK syntax: packages/discord/src/cli.mjs
OK syntax: packages/discord/src/connector.mjs
OK syntax: packages/discord/src/context.mjs
OK syntax: packages/discord/src/engine-pi.mjs
OK syntax: packages/discord/src/errors.mjs
OK syntax: packages/discord/src/gateway.mjs
OK syntax: packages/discord/src/git.mjs
OK syntax: packages/discord/src/journal.mjs
OK syntax: packages/discord/src/notify.mjs
OK syntax: packages/discord/src/rest.mjs
OK syntax: packages/discord/src/setspark.mjs
OK syntax: packages/discord/src/tools.mjs
OK syntax: packages/discord/src/web.mjs
OK syntax: packages/discord/bin/git-credential.mjs
OK syntax: packages/discord/extension/tools.mjs
OK syntax: packages/discord/tests/approvals.test.mjs
OK syntax: packages/discord/tests/authorize.test.mjs
OK syntax: packages/discord/tests/binding.test.mjs
OK syntax: packages/discord/tests/connector.test.mjs
OK syntax: packages/discord/tests/context.test.mjs
OK syntax: packages/discord/tests/engine.test.mjs
OK syntax: packages/discord/tests/fake-pi.mjs
OK syntax: packages/discord/tests/gateway.test.mjs
OK syntax: packages/discord/tests/git.test.mjs
OK syntax: packages/discord/tests/helpers.mjs
OK syntax: packages/discord/tests/journal.test.mjs
OK syntax: packages/discord/tests/notify.test.mjs
OK syntax: packages/discord/tests/recover.test.mjs
OK syntax: packages/discord/tests/rest.test.mjs
OK syntax: packages/discord/tests/setspark.test.mjs
OK syntax: packages/discord/tests/tools.test.mjs
OK syntax: packages/discord/tests/web.test.mjs
OK syntax: packages/discord/fixtures/claim-worker.mjs
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
OK syntax: scripts/discord.sh
OK syntax: scripts/discord-service.sh
OK packages/discord declares no dependencies
OK no bot-token-shaped string in packages/discord
OK fixture binding uses placeholder ids only
OK fixture binding validates
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
OK real pi with protocol vault adds reserve_id to the git verbs
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
OK real pi refuses a git key on a read-only root (fail closed)
OK real pi with the pilot flags (--no-tools) exposes no tool at all
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
OK node --test packages/discord/tests/ (ℹ pass 178)
OK scripts/discord.sh --help exits 0
OK scripts/discord.sh check without a binding exits 4
OK scripts/discord.sh recover without a binding exits 4
OK scripts/discord.sh reload without a binding exits 4
OK scripts/discord-service.sh without a command exits 4
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
OK service install writes the rendered unit (0644) and leaves no temp file
OK service install a second time reports unchanged
OK systemd-analyze verify accepts the rendered unit
OK service uninstall removes the unit file
OK service install with an unknown flag exits 4
OK service install with USER unset finishes and names the account for lingering
discord suite: 66 passed, 0 failed
@@ -0,0 +1,21 @@
OK initial ordinary-file install
OK installed tree matches canonical source
OK installed tree has no symlinks
OK check detects installation drift
OK sync refuses to overwrite installation drift
OK check detects an extra destination file
OK check detects an extra destination directory
OK check rejects a destination symlink
OK sync accepts a canonical source update
OK updated installation matches canonical source
scripts/test-extension-package.sh: line 14: 1734733 Killed "$@" > /dev/null 2>&1
OK forced interruption kills the replacing process
OK next invocation recovers old consistent installation
OK interrupted replacement rolled back
OK sync succeeds after interruption recovery
OK unlocked stale lock file does not block
OK active lock refuses a concurrent sync
OK source symlink fails closed
OK nested second entrypoint fails closed
extension package selftest: 18 passed, 0 failed
@@ -0,0 +1,53 @@
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
OK syntax: scripts/foundation-inspect.mjs
OK syntax: scripts/foundation/strict-json.mjs
OK syntax: scripts/foundation/canonical.mjs
OK syntax: scripts/foundation/resolve.mjs
OK syntax: scripts/foundation/validate-record.mjs
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
OK syntax: scripts/foundation/canonical.test.mjs
OK syntax: scripts/foundation/cli.test.mjs
OK syntax: scripts/foundation/fixtures.test.mjs
OK syntax: scripts/foundation/resolve.test.mjs
OK syntax: scripts/foundation/strict-json.test.mjs
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
OK fixture generator runs
OK checked-in fixtures/bundles equal a fresh generation
OK checked-in fixtures/raw equal a fresh generation
OK checked-in fixtures/index.json equal a fresh generation
OK checked-in demo bundles equal a fresh generation
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
OK node --test scripts/foundation/ (ℹ pass 80)
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
OK oracle: zero schema-column disagreements with the pinned checker
OK oracle: strict-only profile refusals are counted and asserted
OK demo: permitted read preview exits 0 (exit 0)
OK demo: permitted file.change preview exits 0 (exit 0)
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
OK demo: revoked registration is refused (exit 3) (exit 3)
OK demo: message is not authority (exit 3) (exit 3)
OK usage: no arguments exits 2 (exit 2)
OK io: missing file exits 4 (exit 4)
OK io: directory exits 4 (exit 4)
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
OK bound: oversize fixture exits 2 (exit 2)
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
OK text output starts with the disclaimer
OK json output is valid JSON with result allowed and exactly the charter §7 fields
OK json golden matches byte-for-byte
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
OK canary never printed (bundle run and credential-file run)
OK a non-bundle JSON file is refused at the shape gate, not read into output
OK no field of the non-bundle file is echoed
selftest: 44 passed, 0 failed
@@ -0,0 +1,35 @@
toolchain: node v26.8.1, git version 2.55.0
OK syntax: packages/queue/src/cli.mjs
OK syntax: packages/queue/src/errors.mjs
OK syntax: packages/queue/src/io.mjs
OK syntax: packages/queue/src/lock.mjs
OK syntax: packages/queue/src/queue.mjs
OK syntax: packages/queue/src/review.mjs
OK syntax: packages/queue/src/store.mjs
OK syntax: packages/queue/tests/commit.test.mjs
OK syntax: packages/queue/tests/data.test.mjs
OK syntax: packages/queue/tests/dispatch.test.mjs
OK syntax: packages/queue/tests/helpers.mjs
OK syntax: packages/queue/tests/lock.test.mjs
OK syntax: packages/queue/tests/migration.test.mjs
OK syntax: packages/queue/tests/review.test.mjs
OK syntax: packages/queue/tests/store.test.mjs
OK syntax: packages/queue/tests/write.test.mjs
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
OK syntax: scripts/queue-commit.sh
OK syntax: scripts/git-hooks/pre-commit
OK syntax: scripts/mosaic
OK queue-commit.sh, the guard and scripts/mosaic are executable
OK packages/queue declares no dependencies
ℹ tests 148
ℹ pass 148
ℹ fail 0
OK node --test packages/queue/tests/
OK scripts/mosaic queue help
skip queue verify and render --check: this checkout (/home/jwoltje/darkwing-scratch/r53a/cand) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
queue suite: 27 passed, 0 failed
@@ -0,0 +1,18 @@
start 2026-10-10T17:56:30Z
OK valid RELEASE resolves (exit 0)
OK invalid RELEASE exits 1 (exit 1)
OK missing RELEASE exits 1 (exit 1)
OK valid RELEASE leaves image tag consistent with version
OK status safe on empty state (exit 0)
OK status created no pointer
OK fault-injected activation refuses (exit 1)
OK refused activation wrote no pointer
OK refusal logged exactly once with valid fields
OK healthy activation succeeds (exit 0)
OK pointer written with valid fields
OK repeat activation succeeds (log grows) (exit 0)
OK log is append-only across activations
OK rollback without previous refuses (exit 1)
selftest: 14 passed, 0 failed
rc=0 end 2026-10-10T17:56:43Z
@@ -0,0 +1,7 @@
OK valid RELEASE resolves (exit 0)
OK invalid RELEASE exits 1 (exit 1)
OK missing RELEASE exits 1 (exit 1)
OK valid RELEASE leaves image tag consistent with version
skip state-machine cases (docker daemon unavailable)
selftest: 4 passed, 0 failed
@@ -0,0 +1,32 @@
OK valid task validates (exit 0)
OK unknown task key exits 2 (exit 2)
OK unsupported taskVersion exits 2 (exit 2)
OK invalid task id exits 2 (exit 2)
OK empty prompt exits 2 (exit 2)
OK NUL in expectExact exits 2 (exit 2)
OK out-of-range timeout exits 2 (exit 2)
OK missing mission file exits 4 (exit 4)
OK task with valid mission validates (exit 0)
OK invalid mission exits 2 (exit 2)
OK validate missing task exits 4 (exit 4)
OK validation does not modify the task file
OK prune dry-run exits 0 (exit 0)
OK dry-run deleted nothing
OK prune --keep=2 --yes removes oldest (exit 0)
OK kept exactly 2 newest runs
OK newest run kept, oldest pruned
OK append-only receipt written (3 entries)
OK sessions/workspaces untouched by prune
OK prune with invalid keep exits 4 (exit 4)
skip adapter seam cases (docker daemon unavailable)
skip workspace/capability cases (docker daemon unavailable)
skip live task cases (docker unavailable)
OK onboard without name exits 4 (non-interactive) (exit 4)
OK onboard --name renders profile (exit 0)
OK profile written
OK canon structure: required filled, optional placeholdered
OK canon sections present
skip live recall pair: user recall run, recalled user name (docker daemon unavailable)
OK no agent identity on headless run
selftest: 26 passed, 0 failed
@@ -0,0 +1,74 @@
// Darkwing row 53 probes: does kept data show anywhere while the board or the bus refuses?
// Run from a worktree's packages/webui: node --test <this file> with WT set to that worktree.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createServer } from 'node:http';
import { pathToFileURL } from 'node:url';
const WT = process.env.WT;
const u = p => pathToFileURL(`${WT}/packages/webui/${p}`).href;
const { browser } = await import(u('tests/browser.mjs'));
const { close } = await import(u('tests/fixture.mjs'));
const { startServer } = await import(u('src/serve.mjs'));
const { BusReadError } = await import(u('src/bus.mjs'));
const out = [];
const note = (k, v) => { out.push(`${k}: ${JSON.stringify(v)}`); };
test('probes', { timeout: 120000 }, async () => {
let reply = null;
const one = { agent: 'agent1', project: 'secretproj', state: 'waiting', waitingOnYou: true, task: 'Kept fixture task', lastActivity: '2026-10-10T00:00:00Z', lastAssistantText: 'Input needed: kept text', registered: null };
const ok = () => { reply = { status: 200, body: { sessions: [one, { ...one, agent: 'agent2', state: 'working', waitingOnYou: false }], counts: { waiting: 1, working: 1 }, generatedAt: new Date().toISOString() } }; };
ok();
const brd = createServer(async (req, res) => { for await (const c of req) void c; res.writeHead(reply.status, { 'content-type': 'application/json' }); res.end(JSON.stringify(reply.body)); });
await new Promise(r => brd.listen(0, '127.0.0.1', r));
const rows = { inbox: [{ id: 'd'.repeat(32), question: 'Kept question?', class: 'gated', action: 'git.push.protected', blocking: true, options: [{ key: 'yes', text: 'Yes' }], at: new Date().toISOString(), raised_by: 'coder' }], tasks: [{ task_ref: 'vikunja:32/7', fields: { title: 'Kept task title', bucket: 2, done: false, assignees: [] }, source: 'self' }], agents: [], trail: [] };
let fail = null;
const verb = n => async () => { if (fail) throw new BusReadError(fail, `fixture ${fail}`); return structuredClone(rows[n]); };
const web = await startServer({ port: 0, board: `http://127.0.0.1:${brd.address().port}`, bus: { inbox: verb('inbox'), tasks: verb('tasks'), agents: verb('agents'), trail: verb('trail') } });
const base = `http://127.0.0.1:${web.address().port}/`, b = await browser();
const wait = expr => b.evaluate(`(async()=>{for(let i=0;i<100;i++){if(${expr})return true;await new Promise(r=>setTimeout(r,50))}throw new Error('timed out: '+${JSON.stringify(expr)})})()`);
const vis = sel => b.evaluate(`(()=>{const e=document.querySelector(${JSON.stringify(sel)});if(!e)return null;return {shown:!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)&&!e.closest('[hidden]'),text:e.innerText.slice(0,200)}})()`);
const refresh = async () => { await b.evaluate('document.querySelector("#refresh").click()'); await wait('!document.querySelector("#refresh").disabled'); };
try {
await b.viewport(1440, 900);
await b.navigate(base); await wait('document.querySelectorAll("table.sessions tbody tr").length===2');
// P1/P2: open the inspector on agent1, then the board refuses.
await b.evaluate('document.querySelector("table.sessions [data-open]").click()');
await wait('document.querySelector("#inspector-title")?.textContent==="agent1"');
reply = { status: 403, body: { error: 'non-local Host refused' } };
await refresh();
await wait('document.querySelector("#error").className==="banner ref"');
note('P1 board refused: board-view display', await b.evaluate('getComputedStyle(document.querySelector("#board-view")).display'));
note('P1 board refused: inspector', await vis('#inspector'));
note('P2 board refused: projects tree', await vis('#projects'));
note('P2 board refused: footer', await vis('#footer'));
note('P2 board refused: #status', await vis('#status'));
// P1b: Escape from the inspector while refused.
ok(); await refresh(); await wait('document.querySelector("#error").hidden');
// P3/P4: bus pages read once, then the bus refuses.
await b.evaluate('location.hash="#/tasks"'); await wait('document.querySelectorAll("#bus-view td.s1-title a").length===1');
await b.evaluate('location.hash="#/inbox"'); await wait('!!document.querySelector("#bus-view .s1-dec-open")');
fail = 'human-required'; await refresh(); await wait('!!document.querySelector("#bus-view .banner.ref")');
note('P4 bus refused: section list', await vis('#sections'));
await b.evaluate('document.querySelector("#cmdk").click()'); await wait('document.querySelector("#cmdk-dialog").open');
await new Promise(r => setTimeout(r, 500));
note('P3 bus refused: palette list', await b.evaluate('[...document.querySelectorAll("#cmdk-list li")].map(l=>l.innerText.replace(/\\n/g," | "))'));
await b.key('Escape');
// P3b: from the board page while the bus refuses, the Inbox count.
await b.evaluate('location.hash="#/"'); await wait('document.querySelectorAll("table.sessions tbody tr").length===2');
await new Promise(r => setTimeout(r, 500));
note('P4b board page, bus refused: section list', await vis('#sections'));
// P6: not-configured on a page read before.
fail = 'not-configured'; await b.evaluate('location.hash="#/tasks"'); await wait('!!document.querySelector("#bus-view .banner")');
note('P6 not-configured: banner class, task links', await b.evaluate('[document.querySelector("#bus-view .banner").className, document.querySelectorAll("#bus-view td.s1-title a").length]'));
// P7: after a refusal, a non-refusal failure: are rows from before the refusal shown as stale?
fail = 'outcome-unknown'; await refresh(); await wait('!!document.querySelector("#bus-view .banner")');
note('P7 refusal then outcome-unknown: banner class, task links, banner text', await b.evaluate('[document.querySelector("#bus-view .banner").className, document.querySelectorAll("#bus-view td.s1-title a").length, document.querySelector("#bus-view .banner").innerText.slice(0,160)]'));
// P8: toast in the accessibility tree before any copy.
const ax = await b.call('Accessibility.getFullAXTree', {});
note('P8 status/live nodes before any toast', ax.nodes.filter(n => ['status'].includes(n.role?.value)).map(n => n.name?.value ?? ''));
note('P8 toast display', await b.evaluate('getComputedStyle(document.querySelector("#toast")).display'));
} finally {
console.log(out.join('\n'));
await b.close(); await close(web); await new Promise(r => brd.close(r));
}
});
@@ -0,0 +1,21 @@
P1 board refused: board-view display: "none"
P1 board refused: inspector: {"shown":true,"text":"agent1\nClose\nState\nwaiting\nProject\nsecretproj\nTask\nKept fixture task\nTask source\nunknown\nTask set by\nnot applicable (task is not from a registration)\nActive project\nunknown\nWorkspace\nunknown\nModel\nunk"}
P2 board refused: projects tree: {"shown":true,"text":"All projects 2\nsecretproj 2"}
P2 board refused: footer: {"shown":true,"text":"waiting 1 · working 1"}
P2 board refused: #status: {"shown":true,"text":"Scanned 2026-10-10T17:35:07.220Z · refresh every 10s"}
P4 bus refused: section list: {"shown":true,"text":"Board\nInbox\n1\n1 BLOCKING\nTasks\nAgents"}
P3 bus refused: palette list: ["Board | view","Inbox | view","Tasks | view","Agents | view","Kept question? | decision dddddddd","#7 Kept task title | task"]
P4b board page, bus refused: section list: {"shown":true,"text":"Board\nInbox\n1\n1 BLOCKING\nTasks\nAgents"}
P6 not-configured: banner class, task links: ["banner ref",0]
P7 refusal then outcome-unknown: banner class, task links, banner text: ["banner err s1-stale",1,"The last read failed. The bus did not answer in time. (outcome-unknown) This is what was read before, 1 s ago. Nothing was changed.\n\nRead again"]
P8 status/live nodes before any toast: []
P8 toast display: "none"
✔ probes (3409.079539ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 3480.865928
@@ -0,0 +1,204 @@
# Row 53, console tokens and shell restyle, round 1 review (Darkwing)
Issue #1542, request comment 27138, packet comment 27139, queue revs
358-359. Base `8a7871ff`. Candidate manifest sha256
`75569953c1a2cd7b7266208d6e6607290335f1bfe0cf94d96ed224e480a1dda8`, 16
files under `packages/webui`. The packet manifest and the snapshot I took
from the checkout both check 16 OK.
Verdict: **request changes**. The restyle itself is good. Tokens and the
sprite are byte copies, the fonts match the upstream archive, every suite
is green, and 13 of 20 mutants die on assertions. The problem is the
refusal state. The packet says a board 403 "fails closed (kept rows
hidden)" and a bus refusal never shows kept rows. The main table and the
bus view do that. Five other places still show data read before the
refusal: the inspector, the project tree, the footer and the scan line on
the board, and the Inbox count and the Ctrl+K palette on the bus. The
fix is small and listed under "Required".
## Method
- Detached worktrees at `8a7871ff`: `base`, `cand` and `mutwt`. The last
two hold the 16 candidate files, staged, and check 16 OK. `mutwt` still
checks 16 OK after the mutant runs (`r1/mut/manifest-after.txt`).
- `r1/gate.sh`: the `packages/webui` suite, `build-tokens.mjs --check`,
then every `scripts/test-*.sh` with `DOCKER_HOST=unix:///nonexistent.sock`,
17:31:49Z to 17:34:30Z.
- 20 mutants (`r1/mut/mutate.py`, `r1/mut/run.sh`), each against the full
webui suite, 17:37:26Z to 17:56:15Z.
- `test-release` with Docker, finished 17:56:43Z.
- Font provenance: I downloaded `JetBrainsMono-2.304.zip` from the URL in
`jetbrains-mono-sources.txt` into an empty directory and compared hashes
(`r1/fonts/check.txt`).
- Probes in Chromium through the candidate's own `tests/browser.mjs`,
with a fake board and fake bus verbs, no tracker request
(`r1/probes/probe.test.mjs`, output `r1/probes/probes-cand.txt`).
- Read through `bus.js`, `app.js`, `index.html`, `shell.css`, `serve.mjs`
and `shell.test.mjs` against the brief and `IMPLEMENTING.md`.
Node v26.8.1, `TMPDIR=~/darkwing-scratch/r53a/tmp`.
## Suites
| Suite | Result |
|---|---|
| packages/webui (node) | 27/0 |
| build-tokens --check | rc 0, "tokens.css is current" |
| test-auth | 15/0 |
| test-conductor | 17/0 |
| test-config | 24/0 |
| test-discord | 66/0 |
| test-extension-package | 18/0 |
| test-foundation | 44/0 |
| test-queue | 27/0 |
| test-release | 4/0 without Docker, 14/0 with it |
| test-task, Docker unreachable | 26/0 |
I didn't run test-task with real Docker, because it makes live model
calls. The candidate changes nothing outside `packages/webui`, and
Dewey's run gave 98/0.
## Copies and fonts
`tokens.css` and `icons.svg` are byte-equal to `docs/design`. The archive
hashes to `6f6376c6…7bbf`, the value in the sources file. The Regular,
Medium and Bold woff2 files and `OFL.txt` from the archive hash the same
as the shipped files (`a9cb1cd8…`, `086c48df…`, `c503cc5e…`, `30f0c136…`).
## Probes
Fixture: one board with two sessions (project `secretproj`, task
"Kept fixture task"), one inbox decision ("Kept question?") and one task
("Kept task title"). Each probe reads once, then makes the read refuse.
| Probe | Candidate |
|---|---|
| P1 board 403 with the inspector open | `#board-view` is `display:none`, but the inspector stays open with agent1's state, project `secretproj` and task "Kept fixture task" |
| P2 board 403 | the project tree shows "All projects 2 / secretproj 2"; the footer shows "waiting 1 · working 1"; `#status` shows "Scanned <time> · refresh every 10s" |
| P3 bus `human-required` on `#/inbox`, after `#/tasks` was read | the palette lists "Kept question?" and "#7 Kept task title" |
| P4 the same refusal | the section list shows Inbox "1" and "1 BLOCKING" |
| P4b the same on the board page | same as P4 |
| P6 `not-configured` after a good read | `banner ref`, no task rows. Correct |
| P7 refusal, then `outcome-unknown` | the rows from before the refusal come back under the stale banner, "1 s ago". Fine by the brief, see note 6 |
| P8 the toast before any copy | `display:none` |
P1 and P2 come from `body.board-refused #board-view{display:none}`: it
hides the table and nothing else. The inspector, the tree, the footer and
`#status` are outside `#board-view`. P3 and P4 come from the kept cache.
`pkItems()` builds from `last.get('inbox')` and `last.get('tasks')`, and
`pkOpen` refetches with `Promise.allSettled`, so a refused refetch leaves
the old rows in `last`. The Inbox count keeps its last value, because on
the board route a failed `fetchRead('inbox')` lands in `catch {}`.
Base does none of this either. There, every failure showed kept data
under a stale banner, so this isn't a regression. It does contradict the
row's own claim and `IMPLEMENTING.md` line 122 ("refusal (fail closed: the
refusal code in mono and the file or rule that is missing)"). The project
name and the task title in P1 are exactly what a refusal is supposed to
stop showing.
## Mutants
13 of 20 killed (`r1/mut/summary.txt`). Every kill is an assertion or a
wait timeout in the new tests, not a load error.
| Mutant | Change | Result |
|---|---|---|
| M01 | `refusal()` drops `not-configured` and `no-bus-host` | **survived** |
| M02 | `app.js` `error()`: `refused = false` | killed |
| M03 | no `body.board-refused` toggle | killed |
| M04 | the `refused:` code not in `<code>` | survived, equivalent: `why()` still puts the same code in `<code>` |
| M05 | no stale words in the bus freshness line | killed |
| M06 | System sets `data-mode` | killed |
| M07 | toast text not escaped | **survived** |
| M08 | palette label not escaped | **survived** |
| M09 | `:focus-visible` offset 0 | survived |
| M10 | focus outline 1px | killed |
| M11 | the section list below 760px loses `flex-wrap` and `overflow-x` | survived |
| M12 | no section icons | killed |
| M13 | `accept()` doesn't clear `board-refused` | killed |
| M14 | task-link ↑/↓ reversed | killed |
| M15 | `/icons.svg` served as `text/plain` | killed |
| M16 | no `tabular-nums` | survived |
| M17 | storage key changed | killed |
| M18 | no `body.board-refused #board-view` rule | killed |
| M19 | refusal shows kept rows (Dewey's "refusal") | killed |
| M20 | board refusal uses `banner err` | killed |
M01 matters most. Both browser tests refuse with `human-required` only,
so nothing checks that `not-configured` or `no-bus-host` fail closed over
kept rows. My P6 shows the candidate does it, but no test holds it.
M08 is the palette label, which is bus text (`d.question`, the task
title). The code escapes it; no test feeds it markup. M07 is the toast,
whose text is `Copied: <command>`; the commands come from validated refs,
so M07 is lower risk.
## Required
1. Board refusal hides everything read from the board, not only
`#board-view`: close the inspector (or hide it under
`body.board-refused`), and hide or blank the project tree counts, the
footer counts and the "Scanned" line in `#status`.
2. Bus refusal drops the kept bus data outside the view: the Inbox count
and BLOCKING chip in the section list, and the palette's decisions and
tasks. Clearing `last` for `inbox` and `tasks` on a refused read
would cover both, including a refused refetch in `pkOpen` and the
board route's `fetchRead('inbox')`.
3. Tests for 1 and 2, with the inspector open when the board refuses.
4. A refusal over kept rows with `not-configured` or `no-bus-host`, so M01
dies.
5. A palette test whose question or task title holds markup, so M08 dies.
## Notes (not blocking)
1. Below 760px the section list wraps. `IMPLEMENTING.md` asks for "a
horizontal strip that scrolls inside itself". The packet lists it as a
deviation; Sage should rule on it, and M11 shows no test pins either
form.
2. Below 760px the freshness line is `display:none`, so a phone shows no
read time in the command bar. The stale banner still says it in the
view.
3. No test checks the focus offset (M09), and table rows use
`outline-offset:-2px`. Inset rings on rows are reasonable; the brief
says 2px offset.
4. The toast is the only live region for copy, and it is `display:none`
until the first copy. A live region that is out of the accessibility
tree when its text arrives may not be announced the first time. I
couldn't confirm it with a screen reader, so treat it as a risk. The
usual fix is to keep the region rendered and empty, and hide it with
the visually-hidden pattern.
5. The board refusal sets the server's message in `<code>` ("refused:
<code>board-refused</code>" in the test, "non-local Host refused" from
control-board). That is prose in mono. A Seen POST that fails also
goes through `error()`, so a 403 on Seen would say "The board refused
the read". That 403 only comes from control-board's Host/Origin check,
which refuses the read too, so the wording is wrong only in name.
6. After a refusal, the next non-refusal failure shows the rows from
before the refusal as stale (P7). They carry their read time, which is
what the brief asks of the error state. Clearing `last` (Required 2)
would remove them too; either is fine.
7. No `tabular-nums` test (M16), no business pill (row 54), and
`#copy-status` lost `role="status"` (the packet explains why).
## Filbert's review
Filbert posted request changes in comment 27143 at 17:52Z, while my
mutants ran. I read it after writing the above and before posting. Their
C1 and C2 are my Required 1 and 2, found separately with their own probe,
and their T1 is my Required 4 (my M01, their `refusal-403-only`). I agree
with their C3: the board freshness line should say the board wasn't read,
not "stale", which fits under Required 1. I didn't check their N1
(board time local, bus time UTC). It is cheap to settle in the same
round. My Required 5 (M08, the palette escape) isn't in their list.
## Files
- `r1/candidate-manifest.sha256`: copy of Dewey's.
- `r1/gate.sh`, `r1/out/`: suite runs, `summary.txt` and
`test-release-docker.txt`.
- `r1/mut/`: mutant definitions, runner, diffs, outputs, `summary.txt` and
the manifest check after the runs.
- `r1/probes/`: the probe test and its output.
- `r1/fonts/check.txt`: the archive and file hashes, and the two copy
checks.