review(53): Darkwing round 2, request changes (#1542 comment 27165)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
2c1c65b477f03a9474ca13022eb511363ffef0b75b932d404a0098b7f838594a packages/webui/README.md
|
||||
02bbcb68335e80f8822aeea44bc8be49532cd702b325c1ea09ffe95b361a121c 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
|
||||
d8bc0fc7ad10fa188a6f68b925449476af4ca56ba343b24b089fb48cca296295 packages/webui/src/public/bus.js
|
||||
01b456c1ca685e69e2974d66a615b7b8736abbd51aef7a3f014bca3783710430 packages/webui/src/public/icons.svg
|
||||
fbbb51e5669a1f559015e0fdebf6f450b63383070472d2461f0bce9b9723d890 packages/webui/src/public/index.html
|
||||
d7b4d0ca32f07a63265d89131847cbb72c7e532101e693b31b0f38839b22f7e6 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
|
||||
c68acea3c81c1ac67347bae8139632c13dbcfe91207645aa545caa848887be05 packages/webui/tests/shell.test.mjs
|
||||
+18
@@ -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/r53b/tmp DOCKER_HOST=unix:///nonexistent.sock
|
||||
cd ~/darkwing-scratch/r53b/cand
|
||||
O=~/darkwing-scratch/r53b/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 93df4371..f0261468 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;
|
||||
// After a refusal nothing read before is shown anywhere: not in a view, the palette or the Inbox count.
|
||||
function forget() { last.clear(); inboxCount = null; }
|
||||
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.')}`;
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5795.443344ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6268.860495ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8228.406461ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (1675.044775ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (122.850827ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (41.87758ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1966.703581ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (202.62174ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.245643ms)
|
||||
✔ serve refuses a --business value that is not a business id (191.893201ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4996.198675ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3473.451013ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2317.799437ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1821.002855ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5805.992267ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56174.229792ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4942.660505ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23675.677859ms)
|
||||
✔ loopback host and board origin fail closed (10.046586ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (124.822555ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (172.437494ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1879.898ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.519064ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.976932ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (4.470229ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7510.926876ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (7040.739548ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56775.652439
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (7040.739548ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: !!document.querySelector("#bus-view .banner.ref"): undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53b/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/r53b/mutwt/packages/webui/tests/shell.test.mjs:378:9)
|
||||
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 2084ff63..836dfbc2 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -304,7 +304,7 @@
|
||||
// everything drawn from it: the inspector, the project tree, the counts and the footer. Any other
|
||||
// failure keeps the last data with its scan time.
|
||||
function error(err) {
|
||||
- const refused = err.status === 403;
|
||||
+ const refused = false;
|
||||
failed = refused ? 'refused' : true; $('error').hidden = false;
|
||||
if (refused) {
|
||||
data = null; selected = null; project = null;
|
||||
@@ -0,0 +1,59 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5097.83917ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5306.446774ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7791.246228ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (398.914569ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (470.407043ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (175.165999ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1816.384458ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (186.59124ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.932291ms)
|
||||
✔ serve refuses a --business value that is not a business id (174.235634ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5245.016323ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3422.707286ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (4796.44785ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (5298.920714ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5290.556185ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55325.794121ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4859.496965ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (25971.502398ms)
|
||||
✔ loopback host and board origin fail closed (10.250345ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (134.111283ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (108.160287ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1137.333748ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.13576ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.175629ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.598383ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4929.714237ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3800.054159ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55800.902436
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4929.714237ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'banner err'
|
||||
- 'banner ref'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:148: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 2084ff63..4f2b2625 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -315,7 +315,7 @@
|
||||
inspect(); convForm();
|
||||
}
|
||||
$('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 (5712.8413ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6470.833098ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7936.181163ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (301.533225ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (494.643207ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (231.746191ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2469.733966ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (652.021248ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.392475ms)
|
||||
✔ serve refuses a --business value that is not a business id (165.143296ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (6095.538773ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2939.706831ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1245.419871ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1025.221336ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6516.684216ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56090.939503ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6486.190874ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23562.619035ms)
|
||||
✔ loopback host and board origin fail closed (9.883696ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (126.346727ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (111.624361ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1913.882882ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.422763ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.680236ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.722365ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6346.854574ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3733.402259ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56623.460421
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6346.854574ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
'block' !== 'none'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:150: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 93df4371..9bd147b6 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -371,7 +371,7 @@
|
||||
const refusal = err => err.status === 403 || ['not-configured', 'no-bus-host'].includes(err.code);
|
||||
// After a refusal nothing read before is shown anywhere: not in a view, the palette or the Inbox count.
|
||||
function forget() { last.clear(); inboxCount = null; }
|
||||
- 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,58 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5462.633703ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6016.227844ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7161.229825ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (459.662074ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (198.328355ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (128.375723ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2011.124303ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (330.452814ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.363963ms)
|
||||
✔ serve refuses a --business value that is not a business id (257.002802ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5921.250904ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2583.408385ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1776.121129ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (842.197628ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5884.809548ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56135.561582ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4993.429078ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24999.455651ms)
|
||||
✔ loopback host and board origin fail closed (8.987038ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (151.065885ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (150.806081ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (858.291021ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.308606ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.824401ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.562959ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8401.194007ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (1483.639423ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56554.628294
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (1483.639423ms)
|
||||
AssertionError [ERR_ASSERTION]: #/inbox
|
||||
+ actual - expected
|
||||
|
||||
+ '--business'
|
||||
- 'no-bus-host'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:379:16)
|
||||
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: '--business',
|
||||
expected: 'no-bus-host',
|
||||
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 93df4371..0737320a 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -377,7 +377,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 === 'refused' ? 'Bus <span class="warn">not read: refused</span>' : 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,89 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (8079.031307ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (95685.374707ms)
|
||||
✖ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (39939.301906ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (399.29347ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (107.942253ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (183.540352ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2475.56703ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (210.045959ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.605198ms)
|
||||
✔ serve refuses a --business value that is not a business id (196.905686ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (64786.967758ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (27245.695481ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (6126.989522ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (5118.460015ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (9195.480901ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (88101.20953ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (16229.53364ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (46498.641699ms)
|
||||
✔ loopback host and board origin fail closed (9.766364ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (204.824833ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (368.503988ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1135.758555ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.436508ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.838218ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.916424ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (25445.008415ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (12241.439812ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 24
|
||||
ℹ fail 3
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 103416.270276
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/bus-browser.test.mjs:68:1
|
||||
✖ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (39939.301906ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
'BODY' !== 'H1'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/bus-browser.test.mjs:124:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'BODY',
|
||||
expected: 'H1',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (25445.008415ms)
|
||||
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /stale: the last read failed/. Input:
|
||||
|
||||
'Bus read 18:29:18 UTC (1 s ago)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:221: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 18:29:18 UTC (1 s ago)',
|
||||
expected: /stale: the last read failed/,
|
||||
operator: 'match',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (12241.439812ms)
|
||||
AssertionError [ERR_ASSERTION]: #/inbox
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:372: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 18:29:28 UTC (1 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 2084ff63..7c8f15db 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 (15272.623465ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (31722.087198ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (30330.451829ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (2416.562027ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (37.29549ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (18.416107ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1676.411784ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (250.959993ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.099896ms)
|
||||
✔ serve refuses a --business value that is not a business id (180.793544ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (28900.406029ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4313.697934ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2351.575056ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (2446.325824ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (10421.525061ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (73032.592659ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (12366.957176ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (33923.140487ms)
|
||||
✔ loopback host and board origin fail closed (8.196194ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (445.360351ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (152.315148ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (923.203685ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.929341ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.043473ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.771135ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5708.723686ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (19111.162225ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 73383.749377
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5708.723686ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
true !== false
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:108: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 93df4371..c6273452 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -481,7 +481,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 (9677.560125ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5238.868632ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (11020.061708ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (412.713989ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (210.605785ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (68.144102ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2938.448863ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (506.983539ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (567.330888ms)
|
||||
✔ serve refuses a --business value that is not a business id (810.105696ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7229.729607ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (8811.289186ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (4399.608573ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1755.37456ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7441.830627ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (58785.923958ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (9615.513932ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24892.390944ms)
|
||||
✔ loopback host and board origin fail closed (12.008815ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (182.542287ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (212.873578ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1994.917068ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.662111ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.799647ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (3.174412ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (13373.047364ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (6417.896432ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 27
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 59334.66732
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index 93df4371..846b6f36 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -500,7 +500,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,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (6262.461685ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (8323.877976ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (11564.402673ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (2264.208911ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (86.487989ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (69.631861ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3169.117616ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (343.785283ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (532.902486ms)
|
||||
✔ serve refuses a --business value that is not a business id (400.71332ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (6321.331007ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4518.74495ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2848.536998ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1705.987269ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6589.386693ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (60212.056998ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5627.527147ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24935.535973ms)
|
||||
✔ loopback host and board origin fail closed (10.838303ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (707.851249ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (335.465899ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1717.022266ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.259551ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.791564ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (3.433592ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (9944.951011ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (3695.850599ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 60869.027066
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (3695.850599ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
2 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:413: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: 2,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
|
||||
index ed6d2d96..099ad74c 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,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (7609.074415ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (11452.724025ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (12309.820512ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (2304.099495ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (94.109243ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (42.272421ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2371.801182ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (410.928224ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (531.203001ms)
|
||||
✔ serve refuses a --business value that is not a business id (306.449847ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7788.383155ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4662.276458ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1912.219392ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1496.764289ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (8619.000837ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (59599.785751ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (7467.265369ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (25221.11973ms)
|
||||
✔ loopback host and board origin fail closed (11.544617ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (1719.852203ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (185.65663ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1065.79537ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (6.317784ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (14.868056ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (5.161292ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5535.667757ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (6509.811784ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 60160.369576
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5535.667757ms)
|
||||
AssertionError [ERR_ASSERTION]: #cmdk outline offset
|
||||
|
||||
0 !== 2
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:181:125)
|
||||
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: 0,
|
||||
expected: 2,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
|
||||
index ed6d2d96..cc401c3b 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 (10029.216ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (11567.747539ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (12394.616919ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (2135.731454ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (106.029912ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (43.317817ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2315.691295ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (428.29625ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (532.121068ms)
|
||||
✔ serve refuses a --business value that is not a business id (200.505373ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (10797.128621ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3575.186349ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1850.44415ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1664.190106ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (8184.327825ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (59155.373445ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (9016.166527ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (27198.316759ms)
|
||||
✔ loopback host and board origin fail closed (10.638221ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (1333.045642ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (516.069031ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1227.942831ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.552045ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.997839ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (3.263111ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8865.020042ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (5262.681363ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 59903.296319
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8865.020042ms)
|
||||
AssertionError [ERR_ASSERTION]: #cmdk outline 1px
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:181: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 ed6d2d96..a07327e6 100644
|
||||
--- a/packages/webui/src/public/shell.css
|
||||
+++ b/packages/webui/src/public/shell.css
|
||||
@@ -166,7 +166,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 is one strip that scrolls inside itself (IMPLEMENTING.md), so more sections never add rows. */
|
||||
- .s1-sections{display:flex;flex-wrap:nowrap;overflow-x:auto;flex-basis:100%;min-width:0;max-width:100%;gap:2px;padding-bottom:2px;scrollbar-width:thin}
|
||||
+ .s1-sections{display:flex;flex-wrap:wrap;overflow-x:visible;flex-basis:100%;min-width:0;max-width:100%;gap:2px;padding-bottom:2px;scrollbar-width:thin}
|
||||
.s1-sections>li{flex:none}
|
||||
.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,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (6072.849226ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7648.73759ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (10575.99292ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (2314.72617ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (71.737636ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (26.592189ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1969.155253ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (346.614032ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.001693ms)
|
||||
✔ serve refuses a --business value that is not a business id (204.313294ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5650.641432ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4927.264395ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (3456.642762ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (3245.69597ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6179.814923ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56099.101119ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6232.410476ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24265.087834ms)
|
||||
✔ loopback host and board origin fail closed (11.345162ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (383.542915ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (858.113627ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (926.123488ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.490977ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.558934ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (4.898223ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6025.485868ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (5394.399273ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56657.087449
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6025.485868ms)
|
||||
AssertionError [ERR_ASSERTION]: one row at 360px
|
||||
|
||||
2 !== 1
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:204: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: 2,
|
||||
expected: 1,
|
||||
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 93df4371..046ff49e 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 (5401.316742ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5276.112592ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8044.16258ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (1584.166767ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (105.247657ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (53.793812ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2250.629516ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (269.127594ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.269905ms)
|
||||
✔ serve refuses a --business value that is not a business id (219.283891ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4958.475554ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3630.443988ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1294.07507ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1115.599978ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5574.722064ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56570.885372ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5176.575671ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23510.05973ms)
|
||||
✔ loopback host and board origin fail closed (10.17656ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (137.753736ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (88.553236ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1859.386694ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.686138ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.846105ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (4.440777ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5245.349397ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3503.212069ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57151.799923
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5245.349397ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
0 !== 4
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:196: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 2084ff63..39477043 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 (5471.314194ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7878.85227ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8907.311012ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (944.751298ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (84.28557ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (63.12128ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2026.814322ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (240.833469ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.244614ms)
|
||||
✔ serve refuses a --business value that is not a business id (169.201231ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5690.430019ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3845.901684ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1522.881981ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (957.590092ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5842.696786ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56214.274595ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5702.561978ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (26371.006181ms)
|
||||
✔ loopback host and board origin fail closed (7.080199ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (125.853509ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (99.128202ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (957.323889ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.322481ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.617784ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.454045ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5828.413235ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (5116.480007ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56613.263322
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5828.413235ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected "actual" to be strictly unequal to:
|
||||
|
||||
'none'
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:161: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 93df4371..4eb91e82 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -532,7 +532,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 (6928.118795ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (8209.259569ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (12049.913654ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (2984.527328ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (45.725005ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (25.241585ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1825.996919ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (694.772009ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.374368ms)
|
||||
✔ serve refuses a --business value that is not a business id (184.930858ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7523.995068ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4770.585661ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1243.339041ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1088.351142ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7949.559455ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56700.906036ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (7259.807748ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23891.089464ms)
|
||||
✔ loopback host and board origin fail closed (7.728754ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (866.179772ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (126.918795ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (575.777593ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.024718ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.983577ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.447936ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7103.550163ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (5312.733709ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57103.536658
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7103.550163ms)
|
||||
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/r53b/mutwt/packages/webui/tests/shell.test.mjs:224: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 (5776.919786ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5899.647375ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7817.95423ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (304.00469ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (92.68169ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (59.725948ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2162.640231ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (417.956067ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.93184ms)
|
||||
✔ serve refuses a --business value that is not a business id (154.497633ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5253.517004ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3024.904581ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1715.174597ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1329.071054ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5838.646156ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56097.068788ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5288.871253ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24117.940082ms)
|
||||
✔ loopback host and board origin fail closed (9.101418ms)
|
||||
✖ real board fixture passes through WebUI; assets and isolated seen/reply work (100.398615ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (96.109877ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (879.497882ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.300521ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.702869ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.21885ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8260.236526ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3414.935311ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56499.507114
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/serve.test.mjs:22:1
|
||||
✖ real board fixture passes through WebUI; assets and isolated seen/reply work (100.398615ms)
|
||||
AssertionError [ERR_ASSERTION]: /icons.svg
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/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 ed6d2d96..7fb39c59 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,58 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (7032.157582ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5974.415759ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8270.130573ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (480.132084ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (215.284099ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (202.050789ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2541.451512ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (286.306148ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.640541ms)
|
||||
✔ serve refuses a --business value that is not a business id (250.072087ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5934.362349ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3261.510465ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1883.827199ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1497.152757ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6942.276403ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (57525.879316ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6010.051378ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22782.377069ms)
|
||||
✔ loopback host and board origin fail closed (9.765723ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (126.74125ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (141.695296ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1790.575205ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.988386ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.255569ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.41268ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6915.352548ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3708.441788ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 58096.23649
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6915.352548ms)
|
||||
AssertionError [ERR_ASSERTION]: #fresh
|
||||
+ actual - expected
|
||||
|
||||
+ 'normal'
|
||||
- 'tabular-nums'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:197:60)
|
||||
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: 'normal',
|
||||
expected: 'tabular-nums',
|
||||
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 2084ff63..8ccceb1f 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 (7219.196692ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7659.592024ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8632.286954ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (282.571341ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (122.635688ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (75.618193ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2211.018612ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (397.540081ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (566.728797ms)
|
||||
✔ serve refuses a --business value that is not a business id (174.980802ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7757.890315ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2813.782157ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1649.062025ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1061.913097ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7800.58257ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (57377.700776ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (7773.93166ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (25903.694289ms)
|
||||
✔ loopback host and board origin fail closed (10.020241ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (131.068377ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (140.661963ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1249.439995ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (2.059447ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.730261ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.702814ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7911.76887ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3765.63761ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57861.215013
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7911.76887ms)
|
||||
Error: Uncaught: TypeError: Cannot read properties of null (reading 'mode')
|
||||
at <anonymous>:1:62
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53b/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/r53b/mutwt/packages/webui/tests/shell.test.mjs:121: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 ed6d2d96..9b515c06 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 (3927.278203ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4657.75327ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5524.823411ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (257.569233ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (197.475849ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (62.384222ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2352.562117ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (180.530201ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (533.606041ms)
|
||||
✔ serve refuses a --business value that is not a business id (165.095593ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4242.583993ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2828.148222ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1663.099588ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1125.53899ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4488.22519ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53962.334042ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4220.745217ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23229.025406ms)
|
||||
✔ loopback host and board origin fail closed (11.407687ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (184.096852ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (295.763247ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1230.782963ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.468688ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.116895ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (4.445183ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4761.914043ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3437.488223ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54409.271455
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4761.914043ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
'block' !== 'none'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:150: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 93df4371..de1a56b8 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -433,7 +433,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 = refusal(err) ? 'refused' : 'failed'; }
|
||||
if (refusal(err)) forget();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4453.411945ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5264.477386ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6343.176422ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (555.570309ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (154.111616ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (80.416177ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2073.643449ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (267.731498ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.094794ms)
|
||||
✔ serve refuses a --business value that is not a business id (169.493923ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4369.121592ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2242.808825ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1227.780012ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (917.943994ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4861.316701ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55068.800905ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4414.74079ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22879.258682ms)
|
||||
✔ loopback host and board origin fail closed (8.430657ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (122.99485ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (126.993216ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1042.823066ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.949745ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.846898ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.237079ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8600.36373ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (5870.56617ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 25
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55530.663629
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8600.36373ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: !!document.querySelector("#bus-view .banner.ref"): undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53b/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/r53b/mutwt/packages/webui/tests/shell.test.mjs:230: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:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (5870.56617ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: !!document.querySelector("#bus-view .banner.ref"): undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53b/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/r53b/mutwt/packages/webui/tests/shell.test.mjs:378:9)
|
||||
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 2084ff63..e9f550a1 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -314,7 +314,7 @@
|
||||
$('status').textContent = 'Not read: the board refused the read';
|
||||
inspect(); convForm();
|
||||
}
|
||||
- $('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 (3790.126485ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4070.339367ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6162.782857ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (326.590311ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (396.862759ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (110.608731ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2385.844223ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (254.94543ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.223498ms)
|
||||
✔ serve refuses a --business value that is not a business id (220.172057ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4138.868392ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2853.917903ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1258.099466ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (812.16505ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4183.301896ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54639.801943ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4094.058594ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23424.360921ms)
|
||||
✔ loopback host and board origin fail closed (16.59858ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (188.870931ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (278.788458ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1419.616783ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (4.12186ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.602968ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (5.209893ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4052.812383ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2860.652705ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55419.923852
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4052.812383ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'banner err'
|
||||
- 'banner ref'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:148: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/bus.js b/packages/webui/src/public/bus.js
|
||||
index 93df4371..dc273736 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -370,7 +370,7 @@
|
||||
// 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);
|
||||
// After a refusal nothing read before is shown anywhere: not in a view, the palette or the Inbox count.
|
||||
- function forget() { last.clear(); inboxCount = null; }
|
||||
+ function forget() {}
|
||||
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,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3567.402844ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4006.199314ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5272.110688ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (304.233276ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (107.385725ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (58.355469ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2413.175616ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (143.018572ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.454661ms)
|
||||
✔ serve refuses a --business value that is not a business id (225.520972ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3440.64227ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2063.812556ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1146.976215ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (996.933872ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3432.385645ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53753.61663ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3517.316598ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22592.030121ms)
|
||||
✔ loopback host and board origin fail closed (9.548699ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (116.171793ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (100.990811ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1426.440429ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.085029ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.918929ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.183322ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6763.556991ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (1226.800369ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54139.811347
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (1226.800369ms)
|
||||
AssertionError [ERR_ASSERTION]: #/inbox drops the Inbox count on a human-required refusal
|
||||
|
||||
2 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:383:16)
|
||||
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: 2,
|
||||
expected: 0,
|
||||
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 93df4371..ac5ee574 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -423,7 +423,7 @@
|
||||
// The board page keeps its own title; the Inbox count still follows the bus.
|
||||
if (shown !== null) { shown = null; $('bus-view').innerHTML = ''; document.title = 'Mosaic Console'; if (navigated) $('main').focus(); }
|
||||
try { const r = await fetchRead('inbox'); last.set('inbox', r); if (g === gen) { countInbox(r.rows); sections(path); } }
|
||||
- catch (err) { if (refusal(err)) { forget(); if (g === gen) sections(path); } }
|
||||
+ catch {}
|
||||
if (g === gen) schedule();
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3839.150823ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4398.773937ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5161.938687ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (257.233571ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (75.691859ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (70.807746ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2116.92695ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (193.29717ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (549.215635ms)
|
||||
✔ serve refuses a --business value that is not a business id (175.964255ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4205.85625ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2062.291696ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1227.269696ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1221.050447ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4210.391938ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53870.242716ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3848.544339ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22700.192342ms)
|
||||
✔ loopback host and board origin fail closed (8.406871ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (124.60245ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (103.540624ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (893.165438ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.457484ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.707206ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.687255ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7388.987757ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (7624.873854ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54287.706116
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (7624.873854ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: !document.querySelector("#sections .count"): undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53b/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/r53b/mutwt/packages/webui/tests/shell.test.mjs:418:68)
|
||||
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,12 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index 93df4371..76758e26 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -435,7 +435,6 @@
|
||||
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'; }
|
||||
catch { html = failedView(err); state = refusal(err) ? 'refused' : 'failed'; }
|
||||
- if (refusal(err)) forget();
|
||||
}
|
||||
if (g !== gen) return;
|
||||
freshBus(state);
|
||||
@@ -0,0 +1,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3690.442184ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3849.350418ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5179.717435ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (231.525972ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (137.616984ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (62.325904ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2171.007692ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (141.854219ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.99615ms)
|
||||
✔ serve refuses a --business value that is not a business id (160.592221ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3644.327268ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2456.357644ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1296.77647ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1069.763404ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3612.07748ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53490.878162ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3605.370048ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22582.383388ms)
|
||||
✔ loopback host and board origin fail closed (9.211601ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (128.119652ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (107.85881ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (995.745599ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.903771ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.53098ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.427432ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7142.657614ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (1130.262824ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53881.267549
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (1130.262824ms)
|
||||
AssertionError [ERR_ASSERTION]: #/inbox drops the Inbox count on a human-required refusal
|
||||
|
||||
2 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:383:16)
|
||||
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: 2,
|
||||
expected: 0,
|
||||
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 93df4371..49e54847 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -511,7 +511,7 @@
|
||||
pkBack = document.activeElement; pkAt = 0; $('cmdk-input').value = '';
|
||||
pkItems(); pkFill(); d.showModal(); $('cmdk-input').focus();
|
||||
Promise.allSettled(['inbox', 'tasks'].map(async v => last.set(v, await fetchRead(v)))).then(rs => {
|
||||
- if (rs.some(r => r.status === 'rejected' && refusal(r.reason))) { forget(); sections(routeOf().split('?')[0]); }
|
||||
+
|
||||
if (d.open) { pkItems(); pkFill(); }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3094.868838ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4479.963024ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5207.878049ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (253.189333ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (166.673421ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (250.543874ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2711.339811ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (124.064744ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.361869ms)
|
||||
✔ serve refuses a --business value that is not a business id (179.900947ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2787.993908ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2713.629338ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1517.334847ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1262.349937ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2789.431642ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53580.275598ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2782.596251ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22064.392292ms)
|
||||
✔ loopback host and board origin fail closed (7.027509ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (107.473266ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (83.115149ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1860.131739ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.88699ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.960146ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.992866ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6498.656891ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (7332.608632ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54368.682085
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (7332.608632ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: document.querySelector("#cmdk-dialog").open && [...document.querySelectorAll("#cmdk-list [data-href]")].map(e=>e.dataset.href).length===4 && !document.querySelector("#sections .count"): undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r53b/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/r53b/mutwt/packages/webui/tests/shell.test.mjs:405:34)
|
||||
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 2084ff63..510aea43 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -307,7 +307,7 @@
|
||||
const refused = err.status === 403;
|
||||
failed = refused ? 'refused' : true; $('error').hidden = false;
|
||||
if (refused) {
|
||||
- data = null; selected = null; project = null;
|
||||
+ data = null; project = null;
|
||||
$('projects').innerHTML = '<p class="muted">Not read: the board refused the read.</p>';
|
||||
for (const id of ['waiting-count', 'seen-count', 'session-count']) $(id).textContent = '–';
|
||||
$('footer').textContent = 'Board not read: refused';
|
||||
@@ -0,0 +1,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3086.813365ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3278.687202ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5638.664355ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (239.637465ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (197.053056ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (168.899142ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2952.702683ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (114.932507ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.008507ms)
|
||||
✔ serve refuses a --business value that is not a business id (168.452683ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3069.725175ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2812.549981ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1122.606966ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (769.203298ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2877.518379ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53655.195021ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2938.919633ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22116.008047ms)
|
||||
✔ loopback host and board origin fail closed (6.758241ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (112.880418ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (110.760082ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1924.942791ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.902975ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.804197ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.006071ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2930.528977ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3482.984298ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54060.857983
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2930.528977ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:152: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: false,
|
||||
expected: true,
|
||||
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 2084ff63..2de73dc9 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -282,7 +282,7 @@
|
||||
// Freshness line in the command bar: when the board last scanned, and warning words when the last refresh failed.
|
||||
function fresh() {
|
||||
const at = Date.parse(data?.generatedAt), ago = Number.isFinite(at) ? age(Math.max(0, Math.round((Date.now() - at) / 1000))) : null;
|
||||
- if (!data) { $('fresh-board').innerHTML = failed === 'refused' ? 'Board <span class="warn">not read: refused</span>' : failed ? 'Board <span class="warn">not read: the read failed</span>' : 'Board not read yet'; return; }
|
||||
+ if (!data) { $('fresh-board').innerHTML = failed ? 'Board <span class="warn">not read: the read failed</span>' : 'Board not read yet'; return; }
|
||||
const scan = `Board scanned <b>${esc(Number.isFinite(at) ? `${new Date(at).toISOString().slice(11, 19)} UTC` : 'unknown')}</b>${ago ? ` (${ago} ago)` : ''}`;
|
||||
$('fresh-board').innerHTML = failed ? `${scan} · <span class="warn">stale: the last refresh failed</span>` : scan;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (2968.889817ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3226.573849ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5700.168779ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (266.368346ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (289.193528ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (576.468911ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2511.116714ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (430.389135ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.253433ms)
|
||||
✔ serve refuses a --business value that is not a business id (190.157217ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2961.036578ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2837.798308ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1272.634327ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (831.966075ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2864.021743ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53178.274663ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2862.922541ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22346.280842ms)
|
||||
✔ loopback host and board origin fail closed (7.233985ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (103.43982ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (83.580754ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1875.027395ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.192019ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.359592ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.448547ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2864.793151ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3930.695527ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53561.128538
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2864.793151ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'Board not read: the read failed'
|
||||
- 'Board not read: refused'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:156: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: 'Board not read: the read failed',
|
||||
expected: 'Board not read: refused',
|
||||
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 2084ff63..e34ee5fd 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -310,7 +310,6 @@
|
||||
data = null; selected = null; project = null;
|
||||
$('projects').innerHTML = '<p class="muted">Not read: the board refused the read.</p>';
|
||||
for (const id of ['waiting-count', 'seen-count', 'session-count']) $(id).textContent = '–';
|
||||
- $('footer').textContent = 'Board not read: refused';
|
||||
$('status').textContent = 'Not read: the board refused the read';
|
||||
inspect(); convForm();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3246.245856ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3674.93145ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5179.540346ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (239.681337ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (196.812069ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (44.758167ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2933.216401ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (107.108112ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.400306ms)
|
||||
✔ serve refuses a --business value that is not a business id (161.249002ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3218.875059ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2575.330787ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1147.311364ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (804.925523ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2689.75633ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53854.571559ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2860.421809ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22255.535822ms)
|
||||
✔ loopback host and board origin fail closed (8.336514ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (118.861441ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (95.830197ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1834.666373ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.986879ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.98463ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.208649ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2883.477769ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2756.953859ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54274.036903
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2883.477769ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'waiting 1 · working 1'
|
||||
- 'Board not read: refused'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:155: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: 'waiting 1 · working 1',
|
||||
expected: 'Board not read: refused',
|
||||
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 2084ff63..169e8e0c 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -311,7 +311,6 @@
|
||||
$('projects').innerHTML = '<p class="muted">Not read: the board refused the read.</p>';
|
||||
for (const id of ['waiting-count', 'seen-count', 'session-count']) $(id).textContent = '–';
|
||||
$('footer').textContent = 'Board not read: refused';
|
||||
- $('status').textContent = 'Not read: the board refused the read';
|
||||
inspect(); convForm();
|
||||
}
|
||||
$('error').className = refused ? 'banner ref' : 'banner err';
|
||||
@@ -0,0 +1,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3550.51874ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5312.307526ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5616.759717ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (260.854664ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (91.58931ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (159.936555ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2972.254326ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (165.485236ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (547.85034ms)
|
||||
✔ serve refuses a --business value that is not a business id (162.092336ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3539.736538ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3160.834001ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1301.74645ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (826.83122ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2888.780959ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54197.380733ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3020.912636ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22115.319317ms)
|
||||
✔ loopback host and board origin fail closed (9.401662ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (119.854295ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (321.466276ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1709.391542ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.258316ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.899559ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.650272ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2910.736129ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (4240.183759ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54846.778865
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2910.736129ms)
|
||||
AssertionError [ERR_ASSERTION]: The input was expected to not match the regular expression /Scanned/. Input:
|
||||
|
||||
'Scanned 2026-10-10T18:51:40.678Z · refresh every 10s'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:157: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: 'Scanned 2026-10-10T18:51:40.678Z · refresh every 10s',
|
||||
expected: /Scanned/,
|
||||
operator: 'doesNotMatch',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/webui/src/public/app.js b/packages/webui/src/public/app.js
|
||||
index 2084ff63..9a94ec51 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -308,7 +308,6 @@
|
||||
failed = refused ? 'refused' : true; $('error').hidden = false;
|
||||
if (refused) {
|
||||
data = null; selected = null; project = null;
|
||||
- $('projects').innerHTML = '<p class="muted">Not read: the board refused the read.</p>';
|
||||
for (const id of ['waiting-count', 'seen-count', 'session-count']) $(id).textContent = '–';
|
||||
$('footer').textContent = 'Board not read: refused';
|
||||
$('status').textContent = 'Not read: the board refused the read';
|
||||
@@ -0,0 +1,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3943.764665ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4161.06461ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5544.885357ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (621.447301ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (179.59438ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (130.608711ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2724.299629ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (110.308126ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.747596ms)
|
||||
✔ serve refuses a --business value that is not a business id (188.683838ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3892.153976ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2405.469187ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1162.017836ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (811.669296ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2843.374378ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54334.298298ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2861.443553ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22345.735665ms)
|
||||
✔ loopback host and board origin fail closed (6.544532ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (105.279642ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (306.595913ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1904.82471ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.160351ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.292111ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.341858ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2793.054072ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3257.19315ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54777.426057
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (2793.054072ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
4 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:154: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: 4,
|
||||
expected: 0,
|
||||
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 2084ff63..33702149 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -309,7 +309,6 @@
|
||||
if (refused) {
|
||||
data = null; selected = null; project = null;
|
||||
$('projects').innerHTML = '<p class="muted">Not read: the board refused the read.</p>';
|
||||
- for (const id of ['waiting-count', 'seen-count', 'session-count']) $(id).textContent = '–';
|
||||
$('footer').textContent = 'Board not read: refused';
|
||||
$('status').textContent = 'Not read: the board refused the read';
|
||||
inspect(); convForm();
|
||||
@@ -0,0 +1,36 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4287.251342ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4257.078572ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4787.813726ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (248.673864ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (108.850367ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (57.034728ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2123.171508ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (181.909869ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.763166ms)
|
||||
✔ serve refuses a --business value that is not a business id (158.778448ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4196.796291ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1947.944484ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1219.575356ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (966.856189ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4154.261341ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53503.07169ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3990.479246ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22660.736326ms)
|
||||
✔ loopback host and board origin fail closed (8.965093ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (126.518024ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (104.109094ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1039.219515ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.241848ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.50003ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.17149ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6799.567456ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2563.79395ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 27
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53936.307319
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index 93df4371..bfe9075b 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -376,7 +376,7 @@
|
||||
// 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'; };
|
||||
function freshBus(state) {
|
||||
- $('fresh-bus').innerHTML = state === 'refused' ? 'Bus <span class="warn">not read: refused</span>' : state === 'failed' ? 'Bus <span class="warn">not read: the read failed</span>'
|
||||
+ $('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>' : ''}`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4019.454985ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4218.465523ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5722.957436ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (288.58676ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (146.939199ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (263.376856ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2922.82782ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (244.943803ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.490728ms)
|
||||
✔ serve refuses a --business value that is not a business id (308.685115ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4008.930441ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2357.061135ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1100.14734ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (799.501081ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2671.451659ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53877.624865ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2890.013136ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22280.584605ms)
|
||||
✔ loopback host and board origin fail closed (9.459608ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (150.74983ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (98.568173ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1760.354168ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.048301ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.876693ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.620976ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4024.870689ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (2076.962299ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 25
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54355.140712
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4024.870689ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'Bus read 18:54:25 UTC (0 s ago)'
|
||||
- 'Bus not read: refused'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:234: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 18:54:25 UTC (0 s ago)',
|
||||
expected: 'Bus not read: refused',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (2076.962299ms)
|
||||
AssertionError [ERR_ASSERTION]: #/inbox
|
||||
+ actual - expected
|
||||
|
||||
+ 'Bus read 18:54:28 UTC (0 s ago)'
|
||||
- 'Bus not read: refused'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:382:16)
|
||||
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 18:54:28 UTC (0 s ago)',
|
||||
expected: 'Bus not read: refused',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
|
||||
index ed6d2d96..14821b7b 100644
|
||||
--- a/packages/webui/src/public/shell.css
|
||||
+++ b/packages/webui/src/public/shell.css
|
||||
@@ -103,7 +103,7 @@ body.on-bus #board-refused{display:none}
|
||||
/* Toast: one line, polite, bottom centre. */
|
||||
.toast{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);display:flex;align-items:center;gap:10px;background:var(--strong);color:var(--surface);padding:6px 8px 6px 14px;border-radius:var(--r);font-size:13px;z-index:60;max-width:calc(100% - 24px);box-shadow:var(--shadow)}
|
||||
/* Off, the toast stays in the accessibility tree, empty and visually hidden, so the first copy is announced. */
|
||||
-.toast:not(.on){width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;box-shadow:none}
|
||||
+.toast:not(.on){display:none}
|
||||
.toast span{min-width:0;overflow-wrap:anywhere}
|
||||
.toast.err{background:var(--danger)}
|
||||
.toast button{font:inherit;font-weight:600;border:1px solid currentColor;background:none;color:inherit;border-radius:4px;padding:1px 8px;cursor:pointer}
|
||||
@@ -0,0 +1,63 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3328.993527ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3531.78405ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5447.140363ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (269.185034ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (125.783547ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (145.763412ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2793.230461ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (120.744303ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (588.228776ms)
|
||||
✔ serve refuses a --business value that is not a business id (158.710131ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3282.87396ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2349.879429ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1255.661234ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (834.227184ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2771.824123ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53870.749227ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3156.662852ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22133.981151ms)
|
||||
✔ loopback host and board origin fail closed (8.165648ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (133.174467ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (104.076118ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1812.887702ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.313076ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.501953ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.280554ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (3339.870267ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3129.879391ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54285.916301
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (3339.870267ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
+ false,
|
||||
- true,
|
||||
'visible',
|
||||
'',
|
||||
true
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:199: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: [ false, 'visible', '', true ],
|
||||
expected: [ true, 'visible', '', true ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index 93df4371..4ceace5b 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -370,7 +370,7 @@
|
||||
// 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);
|
||||
// After a refusal nothing read before is shown anywhere: not in a view, the palette or the Inbox count.
|
||||
- function forget() { last.clear(); inboxCount = null; }
|
||||
+ function forget() { last.clear(); }
|
||||
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,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3126.358489ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4118.593367ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5006.994875ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (293.322998ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (179.622323ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (244.651528ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2463.711647ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (126.115149ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.077583ms)
|
||||
✔ serve refuses a --business value that is not a business id (153.121325ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2776.188695ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2558.872677ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1138.046042ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1204.449823ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2668.08359ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53852.495895ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2670.260808ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22057.59907ms)
|
||||
✔ loopback host and board origin fail closed (8.782534ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (130.541717ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (140.203928ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1610.160969ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.884644ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.761476ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.839968ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6358.781073ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (1322.190764ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54329.286857
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (1322.190764ms)
|
||||
AssertionError [ERR_ASSERTION]: #/inbox drops the Inbox count on a human-required refusal
|
||||
|
||||
2 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:383:16)
|
||||
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: 2,
|
||||
expected: 0,
|
||||
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 93df4371..c2eacbe1 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -370,7 +370,7 @@
|
||||
// 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);
|
||||
// After a refusal nothing read before is shown anywhere: not in a view, the palette or the Inbox count.
|
||||
- function forget() { last.clear(); inboxCount = null; }
|
||||
+ function forget() { inboxCount = null; }
|
||||
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,65 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3007.173261ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3855.294086ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5615.842981ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (261.282923ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (93.524702ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (152.594397ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3222.083926ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (80.345244ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (575.740134ms)
|
||||
✔ serve refuses a --business value that is not a business id (190.165655ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2882.103601ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2486.2508ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1177.664134ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1284.697782ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2706.579171ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53381.0835ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2781.75392ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22306.183997ms)
|
||||
✔ loopback host and board origin fail closed (7.394334ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (107.914025ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (97.224984ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1885.353532ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.899596ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.989552ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.889945ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6673.23278ms)
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (2369.587424ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53793.045051
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:316:1
|
||||
✖ five states on Board, Inbox, Tasks, Agents and Trail (2369.587424ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'#/',
|
||||
'#/inbox',
|
||||
'#/tasks',
|
||||
'#/agents',
|
||||
+ '#/inbox/dddddddddddddddddddddddddddddddd',
|
||||
+ '#/tasks/vikunja%3A32%2F7',
|
||||
+ '#/tasks/vikunja%3A32%2F8'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:397: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: [ '#/', '#/inbox', '#/tasks', '#/agents', '#/inbox/dddddddddddddddddddddddddddddddd', '#/tasks/vikunja%3A32%2F7', '#/tasks/vikunja%3A32%2F8' ],
|
||||
expected: [ '#/', '#/inbox', '#/tasks', '#/agents' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/app.js b/packages/webui/src/public/app.js
|
||||
index 2084ff63..5ca3d420 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -283,7 +283,7 @@
|
||||
function fresh() {
|
||||
const at = Date.parse(data?.generatedAt), ago = Number.isFinite(at) ? age(Math.max(0, Math.round((Date.now() - at) / 1000))) : null;
|
||||
if (!data) { $('fresh-board').innerHTML = failed === 'refused' ? 'Board <span class="warn">not read: refused</span>' : failed ? 'Board <span class="warn">not read: the read failed</span>' : 'Board not read yet'; return; }
|
||||
- const scan = `Board scanned <b>${esc(Number.isFinite(at) ? `${new Date(at).toISOString().slice(11, 19)} UTC` : 'unknown')}</b>${ago ? ` (${ago} ago)` : ''}`;
|
||||
+ const scan = `Board scanned <b>${esc(Number.isFinite(at) ? new Date(at).toLocaleTimeString([], { hour12: false }) : 'unknown')}</b>${ago ? ` (${ago} ago)` : ''}`;
|
||||
$('fresh-board').innerHTML = failed ? `${scan} · <span class="warn">stale: the last refresh failed</span>` : scan;
|
||||
}
|
||||
setInterval(() => { if (data) fresh(); }, 5000);
|
||||
@@ -0,0 +1,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3672.88716ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3626.292542ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6049.955031ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (298.139781ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (384.361372ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (95.057268ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2630.859361ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (153.24859ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.057319ms)
|
||||
✔ serve refuses a --business value that is not a business id (149.60264ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3646.12638ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2657.957896ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1187.981349ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (824.394033ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3514.633496ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54861.537569ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3495.154258ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22156.108066ms)
|
||||
✔ loopback host and board origin fail closed (6.632866ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (109.752328ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (116.522219ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1874.392891ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.924477ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.681684ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.146795ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (3244.793375ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3494.281686ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55222.326952
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (3244.793375ms)
|
||||
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /^Board scanned \d\d:\d\d:\d\d UTC/. Input:
|
||||
|
||||
'Board scanned 13:57:59 (2s ago)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:128: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: 'Board scanned 13:57:59 (2s ago)',
|
||||
expected: /^Board scanned \d\d:\d\d:\d\d UTC/,
|
||||
operator: 'match',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
|
||||
index ed6d2d96..c3c3d718 100644
|
||||
--- a/packages/webui/src/public/shell.css
|
||||
+++ b/packages/webui/src/public/shell.css
|
||||
@@ -166,7 +166,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 is one strip that scrolls inside itself (IMPLEMENTING.md), so more sections never add rows. */
|
||||
- .s1-sections{display:flex;flex-wrap:nowrap;overflow-x:auto;flex-basis:100%;min-width:0;max-width:100%;gap:2px;padding-bottom:2px;scrollbar-width:thin}
|
||||
+ .s1-sections{display:flex;flex-wrap:nowrap;overflow-x:visible;flex-basis:100%;min-width:0;max-width:100%;gap:2px;padding-bottom:2px;scrollbar-width:thin}
|
||||
.s1-sections>li{flex:none}
|
||||
.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,53 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5211.322674ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5406.834792ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7967.747109ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (291.194633ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (167.355846ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (301.032325ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2370.843823ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (511.36435ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.862393ms)
|
||||
✔ serve refuses a --business value that is not a business id (163.431821ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5370.262527ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4365.992749ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2791.289399ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1223.433429ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5354.865178ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56830.63505ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5434.62527ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22694.896098ms)
|
||||
✔ loopback host and board origin fail closed (9.937835ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (137.366904ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (141.117982ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1498.910339ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.431161ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.986511ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.729744ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5309.354326ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (6092.066387ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57366.349902
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5309.354326ms)
|
||||
AssertionError [ERR_ASSERTION]: strip scrolls at 360px
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:205: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: 'visible',
|
||||
expected: /^(auto|scroll)$/,
|
||||
operator: 'match',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/shell.css b/packages/webui/src/public/shell.css
|
||||
index ed6d2d96..77fc8ff8 100644
|
||||
--- a/packages/webui/src/public/shell.css
|
||||
+++ b/packages/webui/src/public/shell.css
|
||||
@@ -166,7 +166,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 is one strip that scrolls inside itself (IMPLEMENTING.md), so more sections never add rows. */
|
||||
- .s1-sections{display:flex;flex-wrap:nowrap;overflow-x:auto;flex-basis:100%;min-width:0;max-width:100%;gap:2px;padding-bottom:2px;scrollbar-width:thin}
|
||||
+ .s1-sections{display:flex;flex-wrap:wrap;overflow-x:auto;flex-basis:100%;min-width:0;max-width:100%;gap:2px;padding-bottom:2px;scrollbar-width:thin}
|
||||
.s1-sections>li{flex:none}
|
||||
.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,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5754.769823ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5803.927596ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8809.823038ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (355.250288ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (113.044848ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (74.932958ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2169.690829ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (700.614062ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.626355ms)
|
||||
✔ serve refuses a --business value that is not a business id (157.707334ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5687.132751ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4440.235802ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2682.046974ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (2581.852312ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5716.272685ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (57961.778739ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5798.600313ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23168.713365ms)
|
||||
✔ loopback host and board origin fail closed (10.029846ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (119.87584ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (116.102285ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1062.369733ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.26065ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.576684ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.555527ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5642.483696ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (5046.143675ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 26
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 58384.246613
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:88:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5642.483696ms)
|
||||
AssertionError [ERR_ASSERTION]: one row at 360px
|
||||
|
||||
2 !== 1
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r53b/mutwt/packages/webui/tests/shell.test.mjs:204: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: 2,
|
||||
expected: 1,
|
||||
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,48 @@
|
||||
# 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:nowrap;overflow-x:auto;", " .s1-sections{display:flex;flex-wrap:wrap;overflow-x:visible;")],
|
||||
"M21": [(B, "function forget() { last.clear(); inboxCount = null; }", "function forget() {}")],
|
||||
"M22": [(B, "catch (err) { if (refusal(err)) { forget(); if (g === gen) sections(path); } }", "catch {}")],
|
||||
"M23": [(B, " if (refusal(err)) forget();\n", "")],
|
||||
"M24": [(B, "if (rs.some(r => r.status === 'rejected' && refusal(r.reason))) { forget(); sections(routeOf().split('?')[0]); }", "")],
|
||||
"M25": [(A, "data = null; selected = null; project = null;", "data = null; project = null;")],
|
||||
"M26": [(A, "failed === 'refused' ? 'Board <span class=\"warn\">not read: refused</span>' : ", "")],
|
||||
"M27": [(A, " $('footer').textContent = 'Board not read: refused';\n", "")],
|
||||
"M28": [(A, " $('status').textContent = 'Not read: the board refused the read';\n", "")],
|
||||
"M29": [(A, " $('projects').innerHTML = '<p class=\"muted\">Not read: the board refused the read.</p>';\n", "")],
|
||||
"M30": [(A, " for (const id of ['waiting-count', 'seen-count', 'session-count']) $(id).textContent = '–';\n", "")],
|
||||
"M31": [(B, "state === 'refused' ? 'Bus <span class=\"warn\">not read: refused</span>' : ", "")],
|
||||
"M32": [(C, ".toast:not(.on){width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;box-shadow:none}", ".toast:not(.on){display:none}")],
|
||||
"M33": [(B, "function forget() { last.clear(); inboxCount = null; }", "function forget() { last.clear(); }")],
|
||||
"M34": [(B, "function forget() { last.clear(); inboxCount = null; }", "function forget() { inboxCount = null; }")],
|
||||
"M35": [(A, "`${new Date(at).toISOString().slice(11, 19)} UTC`", "new Date(at).toLocaleTimeString([], { hour12: false })")],
|
||||
"M36": [(C, " .s1-sections{display:flex;flex-wrap:nowrap;overflow-x:auto;", " .s1-sections{display:flex;flex-wrap:nowrap;overflow-x:visible;")],
|
||||
"M37": [(C, " .s1-sections{display:flex;flex-wrap:nowrap;overflow-x:auto;", " .s1-sections{display:flex;flex-wrap:wrap;overflow-x:auto;")],
|
||||
"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
@@ -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/r53b/tmp
|
||||
S=~/darkwing-scratch/r53b; 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,40 @@
|
||||
start 2026-10-10T18:25:08Z
|
||||
M01 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M02 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M03 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M04 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M05 killed exit=1 ℹ pass 24 ℹ fail 3
|
||||
M06 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M07 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
|
||||
M08 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M09 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M10 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M11 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
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 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
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
|
||||
M21 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M22 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M23 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M24 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M25 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M26 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M27 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M28 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M29 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M30 SURVIVED exit=0 ℹ pass 27 ℹ fail 0
|
||||
M31 killed exit=1 ℹ pass 25 ℹ fail 2
|
||||
M32 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M33 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M34 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M35 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M36 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
M37 killed exit=1 ℹ pass 26 ℹ fail 1
|
||||
end 2026-10-10T19:00:50Z
|
||||
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 (5523.436991ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (8766.561351ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8827.954235ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (277.316884ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (201.217171ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (73.461855ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2813.609766ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (175.887468ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.046089ms)
|
||||
✔ serve refuses a --business value that is not a business id (197.251891ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (6431.465011ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3853.631876ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (3922.98223ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (3482.629009ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5496.850194ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (58053.664671ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5499.821805ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (25062.757457ms)
|
||||
✔ loopback host and board origin fail closed (9.306713ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (134.500699ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (127.67224ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1474.550223ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.345771ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.627495ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.591051ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8724.52559ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (7349.679734ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 27
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 58535.525593
|
||||
@@ -0,0 +1,391 @@
|
||||
diff -ru /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/README.md packages/webui/README.md
|
||||
--- /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/README.md 2026-10-10 12:23:48.000000000 -0500
|
||||
+++ packages/webui/README.md 2026-10-10 13:15:05.000000000 -0500
|
||||
@@ -91,7 +91,9 @@
|
||||
open sections and the copy status survive a refresh.
|
||||
- A failed read keeps the last good answer for that read and says it is old,
|
||||
with the time it was read. A refusal (403, `not-configured` or `no-bus-host`)
|
||||
- never shows kept rows (row 53). A page never read, or refused, shows the failure instead,
|
||||
+ never shows kept rows (row 53), and it drops every kept bus read: the Inbox
|
||||
+ count and the palette's decisions and tasks go too, until a read succeeds.
|
||||
+ A page never read, or refused, shows the failure instead,
|
||||
titled "Bus refused the read" (403: the bus refused the Console's read, for
|
||||
example `human-required` from a Console started inside an agent run), "No bus to read" (no system
|
||||
config, or no bus host and no `--business`) or "The read failed".
|
||||
@@ -120,12 +122,16 @@
|
||||
and tasks; arrows move, Enter opens, Esc closes and returns focus. Arrow
|
||||
keys also move between task links in the bus tables.
|
||||
- Copy reports in a toast. If the clipboard is unavailable, the toast stays
|
||||
- until dismissed and the command is selected for Ctrl+C.
|
||||
-- The command bar shows when the board was scanned and the bus read, and
|
||||
- says when the last attempt failed. A board refusal hides the board rather
|
||||
- than showing the last scan.
|
||||
+ until dismissed and the command is selected for Ctrl+C. When no toast shows,
|
||||
+ the toast is an empty live region, visually hidden but still in the
|
||||
+ accessibility tree, so the first copy is announced.
|
||||
+- The command bar shows, in UTC, when the board was scanned and the bus read,
|
||||
+ and says when the last attempt failed. A board refusal drops the last scan
|
||||
+ and everything drawn from it: the table, the inspector, the project tree,
|
||||
+ the counts, the footer and the scan line all say the board was not read.
|
||||
- From 760px the footer is a one-line status bar and the side column fits
|
||||
- between it and the command bar. Below 760px the section list wraps.
|
||||
+ between it and the command bar. Below 760px the section list is one strip
|
||||
+ that scrolls inside itself.
|
||||
|
||||
## Data and boundaries
|
||||
|
||||
@@ -204,11 +210,15 @@
|
||||
|
||||
Shell tests (`shell.test.mjs`) check the copies, the font hashes and the kept
|
||||
strings, and in the browser at 400px: System mode under an emulated dark and
|
||||
-light OS, the empty, stale and refused states of the board and the bus views,
|
||||
-not found, the palette, the toast, keyboard and visible focus, no sideways
|
||||
+light OS, the empty, stale and refused states of the board and the bus views
|
||||
+(a board refusal with the inspector open), not found, the palette, the toast,
|
||||
+keyboard and visible focus, the section strip at 360 and 400px, no sideways
|
||||
scroll, no script error and no request other than GET. A second test walks
|
||||
the Board, Inbox, Tasks, Agents and a decision trail through loading (a held
|
||||
-read), normal, a failed read over kept rows, and a refusal.
|
||||
+read), normal, a failed read over kept rows, and a refusal by the bus
|
||||
+(`human-required`) and for want of one (`no-bus-host`). It then checks that a
|
||||
+refusal empties the palette and the Inbox count, from a view, from the
|
||||
+palette's own reads and from the board page, and that palette labels are text.
|
||||
|
||||
No root CI workflow is configured for this package. These local tests are not a
|
||||
claim of CI, deployment, live-seat delivery or user acceptance.
|
||||
diff -ru /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/src/public/app.js packages/webui/src/public/app.js
|
||||
--- /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/src/public/app.js 2026-10-10 12:03:18.000000000 -0500
|
||||
+++ packages/webui/src/public/app.js 2026-10-10 13:15:05.000000000 -0500
|
||||
@@ -282,8 +282,8 @@
|
||||
// Freshness line in the command bar: when the board last scanned, and warning words when the last refresh failed.
|
||||
function fresh() {
|
||||
const at = Date.parse(data?.generatedAt), ago = Number.isFinite(at) ? age(Math.max(0, Math.round((Date.now() - at) / 1000))) : null;
|
||||
- if (!data) { $('fresh-board').innerHTML = failed ? 'Board <span class="warn">not read: the read failed</span>' : 'Board not read yet'; return; }
|
||||
- const scan = `Board scanned <b>${esc(Number.isFinite(at) ? new Date(at).toLocaleTimeString([], { hour12: false }) : 'unknown')}</b>${ago ? ` (${ago} ago)` : ''}`;
|
||||
+ if (!data) { $('fresh-board').innerHTML = failed === 'refused' ? 'Board <span class="warn">not read: refused</span>' : failed ? 'Board <span class="warn">not read: the read failed</span>' : 'Board not read yet'; return; }
|
||||
+ const scan = `Board scanned <b>${esc(Number.isFinite(at) ? `${new Date(at).toISOString().slice(11, 19)} UTC` : 'unknown')}</b>${ago ? ` (${ago} ago)` : ''}`;
|
||||
$('fresh-board').innerHTML = failed ? `${scan} · <span class="warn">stale: the last refresh failed</span>` : scan;
|
||||
}
|
||||
setInterval(() => { if (data) fresh(); }, 5000);
|
||||
@@ -300,10 +300,20 @@
|
||||
for (const id of awaiting.keys()) { const r = row(id); if (r && !pending(r)) awaiting.delete(id); }
|
||||
render();
|
||||
}
|
||||
- // 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.
|
||||
+ // A refusal (403) fails closed: the last data is dropped, not kept or shown as current, and so is
|
||||
+ // everything drawn from it: the inspector, the project tree, the counts and the footer. Any other
|
||||
+ // failure keeps the last data with its scan time.
|
||||
function error(err) {
|
||||
- failed = true; $('error').hidden = false;
|
||||
const refused = err.status === 403;
|
||||
+ failed = refused ? 'refused' : true; $('error').hidden = false;
|
||||
+ if (refused) {
|
||||
+ data = null; selected = null; project = null;
|
||||
+ $('projects').innerHTML = '<p class="muted">Not read: the board refused the read.</p>';
|
||||
+ for (const id of ['waiting-count', 'seen-count', 'session-count']) $(id).textContent = '–';
|
||||
+ $('footer').textContent = 'Board not read: refused';
|
||||
+ $('status').textContent = 'Not read: the board refused the read';
|
||||
+ inspect(); convForm();
|
||||
+ }
|
||||
$('error').className = refused ? 'banner ref' : 'banner err';
|
||||
$('board-refused').hidden = !refused; document.body.classList.toggle('board-refused', refused);
|
||||
$('error').innerHTML = refused
|
||||
diff -ru /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/src/public/bus.js packages/webui/src/public/bus.js
|
||||
--- /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/src/public/bus.js 2026-10-10 11:58:46.000000000 -0500
|
||||
+++ packages/webui/src/public/bus.js 2026-10-10 13:15:05.000000000 -0500
|
||||
@@ -369,12 +369,14 @@
|
||||
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);
|
||||
+ // After a refusal nothing read before is shown anywhere: not in a view, the palette or the Inbox count.
|
||||
+ function forget() { last.clear(); inboxCount = null; }
|
||||
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.
|
||||
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>'
|
||||
+ $('fresh-bus').innerHTML = state === 'refused' ? 'Bus <span class="warn">not read: refused</span>' : 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>' : ''}`;
|
||||
}
|
||||
|
||||
@@ -420,7 +422,8 @@
|
||||
if (board) {
|
||||
// The board page keeps its own title; the Inbox count still follows the bus.
|
||||
if (shown !== null) { shown = null; $('bus-view').innerHTML = ''; document.title = 'Mosaic Console'; if (navigated) $('main').focus(); }
|
||||
- try { const r = await fetchRead('inbox'); last.set('inbox', r); if (g === gen) { countInbox(r.rows); sections(path); } } catch {}
|
||||
+ try { const r = await fetchRead('inbox'); last.set('inbox', r); if (g === gen) { countInbox(r.rows); sections(path); } }
|
||||
+ catch (err) { if (refusal(err)) { forget(); if (g === gen) sections(path); } }
|
||||
if (g === gen) schedule();
|
||||
return;
|
||||
}
|
||||
@@ -431,7 +434,8 @@
|
||||
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'; }
|
||||
- catch { html = failedView(err); state = 'failed'; }
|
||||
+ catch { html = failedView(err); state = refusal(err) ? 'refused' : 'failed'; }
|
||||
+ if (refusal(err)) forget();
|
||||
}
|
||||
if (g !== gen) return;
|
||||
freshBus(state);
|
||||
@@ -506,7 +510,10 @@
|
||||
if (d.open) return;
|
||||
pkBack = document.activeElement; pkAt = 0; $('cmdk-input').value = '';
|
||||
pkItems(); pkFill(); d.showModal(); $('cmdk-input').focus();
|
||||
- Promise.allSettled(['inbox', 'tasks'].map(async v => last.set(v, await fetchRead(v)))).then(() => { if (d.open) { pkItems(); pkFill(); } });
|
||||
+ Promise.allSettled(['inbox', 'tasks'].map(async v => last.set(v, await fetchRead(v)))).then(rs => {
|
||||
+ if (rs.some(r => r.status === 'rejected' && refusal(r.reason))) { forget(); sections(routeOf().split('?')[0]); }
|
||||
+ if (d.open) { pkItems(); pkFill(); }
|
||||
+ });
|
||||
}
|
||||
function pkGo(href) {
|
||||
pkBack = null; $('cmdk-dialog').close();
|
||||
diff -ru /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/src/public/shell.css packages/webui/src/public/shell.css
|
||||
--- /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/src/public/shell.css 2026-10-10 12:14:49.000000000 -0500
|
||||
+++ packages/webui/src/public/shell.css 2026-10-10 13:15:05.000000000 -0500
|
||||
@@ -101,8 +101,9 @@
|
||||
body.on-bus #board-refused{display:none}
|
||||
|
||||
/* Toast: one line, polite, bottom centre. */
|
||||
-.toast{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);display:none;align-items:center;gap:10px;background:var(--strong);color:var(--surface);padding:6px 8px 6px 14px;border-radius:var(--r);font-size:13px;z-index:60;max-width:calc(100% - 24px);box-shadow:var(--shadow)}
|
||||
-.toast.on{display:flex}
|
||||
+.toast{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);display:flex;align-items:center;gap:10px;background:var(--strong);color:var(--surface);padding:6px 8px 6px 14px;border-radius:var(--r);font-size:13px;z-index:60;max-width:calc(100% - 24px);box-shadow:var(--shadow)}
|
||||
+/* Off, the toast stays in the accessibility tree, empty and visually hidden, so the first copy is announced. */
|
||||
+.toast:not(.on){width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;box-shadow:none}
|
||||
.toast span{min-width:0;overflow-wrap:anywhere}
|
||||
.toast.err{background:var(--danger)}
|
||||
.toast button{font:inherit;font-weight:600;border:1px solid currentColor;background:none;color:inherit;border-radius:4px;padding:1px 8px;cursor:pointer}
|
||||
@@ -164,8 +165,9 @@
|
||||
.cmd-right{margin-left:0;flex:1 1 100%;flex-wrap:nowrap;min-width:0}
|
||||
.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}
|
||||
+ /* The section list is one strip that scrolls inside itself (IMPLEMENTING.md), so more sections never add rows. */
|
||||
+ .s1-sections{display:flex;flex-wrap:nowrap;overflow-x:auto;flex-basis:100%;min-width:0;max-width:100%;gap:2px;padding-bottom:2px;scrollbar-width:thin}
|
||||
+ .s1-sections>li{flex:none}
|
||||
.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}
|
||||
}
|
||||
diff -ru /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/tests/shell.test.mjs packages/webui/tests/shell.test.mjs
|
||||
--- /home/jwoltje/darkwing-scratch/r53a/snap/packages/webui/tests/shell.test.mjs 2026-10-10 12:23:48.000000000 -0500
|
||||
+++ packages/webui/tests/shell.test.mjs 2026-10-10 13:15:05.000000000 -0500
|
||||
@@ -35,17 +35,21 @@
|
||||
});
|
||||
|
||||
test('shipped strings are unchanged, and the web keeps its write boundary', () => {
|
||||
- // Markup in the source (<code>, <b>) is not part of the string.
|
||||
+ // Markup in the source (<code>, <b>) is not part of the string. Each string is counted in the
|
||||
+ // file that ships it, so dropping one of two copies fails too.
|
||||
const src = ['app.js', 'bus.js', 'index.html'].map(f => read(f).toString()).join('\n');
|
||||
- const plain = src.replace(/<\/?(code|b)>/g, '');
|
||||
- for (const s of [
|
||||
- 'Copied: ${text}',
|
||||
- 'The clipboard is unavailable. The command is selected; press Ctrl+C to copy it.',
|
||||
- 'Decisions are answered in a terminal with mosaic decide; Console only shows them.',
|
||||
- 'No task ${txt(ref)} in this business.', 'No decision with id ${txt(id)}.', '${txt(b)} is not a decision id.', '${txt(b)} is not a task reference.',
|
||||
- 'No page at this address.', 'Nothing is waiting on you.', 'No tasks in this business yet.', 'Tasks appear after the PM creates them or the poller reads them from Vikunja.',
|
||||
- 'No open tasks.', 'No role is held right now.', 'Nothing of this kind in the trail.', 'Console shows nothing rather than a guess. Nothing was written.',
|
||||
- ]) assert.ok(plain.includes(s), s);
|
||||
+ const plain = Object.fromEntries(['app.js', 'bus.js'].map(f => [f, read(f).toString().replace(/<\/?(code|b)>/g, '')]));
|
||||
+ for (const [file, s, n = 1] of [
|
||||
+ ['bus.js', 'Copied: ${text}'],
|
||||
+ ['bus.js', 'The clipboard is unavailable. The command is selected; press Ctrl+C to copy it.'],
|
||||
+ ['bus.js', 'Decisions are answered in a terminal with mosaic decide; Console only shows them.'],
|
||||
+ ['bus.js', 'No task ${txt(ref)} in this business.', 2], ['bus.js', 'No decision with id ${txt(id)}.'],
|
||||
+ ['bus.js', '${txt(b)} is not a decision id.'], ['bus.js', '${txt(b)} is not a task reference.'],
|
||||
+ ['bus.js', 'No page at this address.'], ['app.js', 'Nothing is waiting on you.'], ['bus.js', 'Nothing is waiting on you.'],
|
||||
+ ['bus.js', 'No tasks in this business yet.'], ['bus.js', 'Tasks appear after the PM creates them or the poller reads them from Vikunja.'],
|
||||
+ ['bus.js', 'No open tasks.'], ['bus.js', 'No role is held right now.'], ['bus.js', 'Nothing of this kind in the trail.'],
|
||||
+ ['bus.js', 'Console shows nothing rather than a guess. Nothing was written.'],
|
||||
+ ]) assert.equal(plain[file].split(s).length - 1, n, `${file}: ${s}`);
|
||||
// No Resolve control and no bus write: the only POSTs are the board's Seen and reply.
|
||||
assert.doesNotMatch(src, /<button[^>]*>[^<]*(Resolve|Decide|Approve)/i);
|
||||
assert.doesNotMatch(read('bus.js').toString(), /method:\s*['"](POST|PUT|PATCH|DELETE)/);
|
||||
@@ -121,7 +125,7 @@
|
||||
// Board, empty: what would appear and where it comes from.
|
||||
assert.match(await text('#sessions .empty'), /No sessions match these filters\.Sessions appear here when the board scan finds/);
|
||||
assert.match(await text('#waiting .empty'), /Nothing is waiting on you\./);
|
||||
- assert.match(await text('#fresh-board'), /^Board scanned \d\d:\d\d:\d\d/);
|
||||
+ assert.match(await text('#fresh-board'), /^Board scanned \d\d:\d\d:\d\d UTC/);
|
||||
await flat(); await shot('board-empty-400');
|
||||
|
||||
// Board, a failed read over kept data: the data stays, labelled with its scan time, and the bar says stale.
|
||||
@@ -135,16 +139,30 @@
|
||||
assert.match(await text('#fresh-board'), /stale: the last refresh failed/);
|
||||
await flat(); await shot('board-stale-400');
|
||||
|
||||
- // Board, a refusal: fails closed, the kept rows are hidden, not shown as current.
|
||||
+ // Board, a refusal: fails closed. The kept rows are hidden, and so is everything drawn from
|
||||
+ // them: the open inspector, the project tree's counts and the footer.
|
||||
+ await b.evaluate('document.querySelector("table.sessions [data-open]").click()'); await wait('!document.querySelector("#inspector").hidden');
|
||||
+ assert.equal(await text('#footer'), 'waiting 1 · working 1');
|
||||
+ assert.equal(await b.evaluate('document.querySelectorAll("#projects .count").length'), 2);
|
||||
brd.refuse(); await refresh();
|
||||
assert.equal(await b.evaluate('document.querySelector("#error").className'), 'banner ref');
|
||||
assert.match(await text('#error'), /The board refused the read\. refused: board-refused/);
|
||||
assert.equal(await b.evaluate('getComputedStyle(document.querySelector("#board-view")).display'), 'none');
|
||||
assert.equal(await b.evaluate('document.querySelector("#board-refused").hidden'), false);
|
||||
+ assert.equal(await b.evaluate('document.querySelector("#inspector").hidden'), true);
|
||||
+ assert.equal(await b.evaluate('document.body.classList.contains("has-inspector")'), false);
|
||||
+ assert.equal(await b.evaluate('document.querySelectorAll("#projects .count, #projects [data-project]").length'), 0);
|
||||
+ assert.equal(await text('#footer'), 'Board not read: refused');
|
||||
+ assert.equal(await text('#fresh-board'), 'Board not read: refused');
|
||||
+ assert.doesNotMatch(await text('#status'), /Scanned/);
|
||||
await flat(); await shot('board-refused-400');
|
||||
brd.one(); await refresh();
|
||||
assert.equal(await b.evaluate('document.querySelector("#error").hidden'), true);
|
||||
assert.notEqual(await b.evaluate('getComputedStyle(document.querySelector("#board-view")).display'), 'none');
|
||||
+ assert.equal(await text('#footer'), 'waiting 1 · working 1');
|
||||
+ // A good read after a failed one drops the stale words.
|
||||
+ brd.fail(); await refresh(); assert.match(await text('#fresh-board'), /stale/);
|
||||
+ brd.one(); await refresh(); assert.match(await text('#fresh-board'), /^Board scanned \d\d:\d\d:\d\d UTC/); assert.doesNotMatch(await text('#fresh-board'), /stale/);
|
||||
|
||||
// Keyboard on the board: down moves between rows, Enter opens, Esc closes and returns focus.
|
||||
await b.evaluate('document.querySelector("table.sessions [data-open]").focus()');
|
||||
@@ -159,12 +177,16 @@
|
||||
await b.evaluate('document.querySelector(".skip").focus();document.querySelector(".skip").blur()');
|
||||
for (const sel of ['#cmdk', '#sections a', '#refresh']) {
|
||||
await b.evaluate(`document.querySelector(${JSON.stringify(sel)}).focus()`);
|
||||
- const ring = await b.evaluate(`(()=>{const c=getComputedStyle(document.activeElement);return [c.outlineStyle, parseFloat(c.outlineWidth)]})()`);
|
||||
- assert.notEqual(ring[0], 'none', `${sel} focus ring`); assert.ok(ring[1] >= 2, `${sel} outline ${ring[1]}px`);
|
||||
+ const ring = await b.evaluate(`(()=>{const c=getComputedStyle(document.activeElement);return [c.outlineStyle, parseFloat(c.outlineWidth), parseFloat(c.outlineOffset)]})()`);
|
||||
+ assert.notEqual(ring[0], 'none', `${sel} focus ring`); assert.ok(ring[1] >= 2, `${sel} outline ${ring[1]}px`); assert.equal(ring[2], 2, `${sel} outline offset`);
|
||||
}
|
||||
|
||||
// Bus pages, empty: each says what would appear and where it comes from.
|
||||
- for (const [hash, h1, words] of [['#/inbox', 'Inbox', /Nothing is waiting on you\./], ['#/tasks', 'Tasks', /No tasks in this business yet\./], ['#/agents', 'Agents', /No role is held right now\./]]) {
|
||||
+ for (const [hash, h1, words] of [
|
||||
+ ['#/inbox', 'Inbox', /^Nothing is waiting on you\.A decision shows here when an agent raises one routed to you through the bus\.$/],
|
||||
+ ['#/tasks', 'Tasks', /^No tasks in this business yet\.Tasks appear after the PM creates them or the poller reads them from Vikunja\.$/],
|
||||
+ ['#/agents', 'Agents', /^No role is held right now\.A role shows here while a run holds its claim on the bus\.$/],
|
||||
+ ]) {
|
||||
await go(hash, h1);
|
||||
assert.match(await text('#bus-view .empty'), words, hash);
|
||||
assert.match(await text('#fresh-bus'), /^Bus read \d\d:\d\d:\d\d UTC/, hash);
|
||||
@@ -172,11 +194,23 @@
|
||||
}
|
||||
assert.equal(await b.evaluate('getComputedStyle(document.querySelector("#fresh-board")).display'), 'none');
|
||||
assert.equal(await b.evaluate('document.querySelectorAll("#sections svg use").length'), 4);
|
||||
+ for (const sel of ['#fresh', '#session-count']) assert.equal(await b.evaluate(`getComputedStyle(document.querySelector(${JSON.stringify(sel)})).fontVariantNumeric`), 'tabular-nums', sel);
|
||||
+ // Before any copy the toast is an empty live region still in the accessibility tree, out of sight.
|
||||
+ assert.deepEqual(await b.evaluate('(()=>{const t=document.querySelector("#toast"),c=getComputedStyle(t),r=t.getBoundingClientRect();return [c.display!=="none",c.visibility,t.textContent,r.width<=1&&r.height<=1]})()'), [true, 'visible', '', true]);
|
||||
+ // Below 760px the section list is one strip that scrolls inside itself: every link on one
|
||||
+ // line, no label wrapped, and still no sideways scroll on the page. 360px, then back to 400px.
|
||||
+ for (const w of [360, 400]) {
|
||||
+ await b.viewport(w, 900);
|
||||
+ assert.equal(await b.evaluate('new Set([...document.querySelectorAll("#sections a")].map(a=>a.offsetTop)).size'), 1, `one row at ${w}px`);
|
||||
+ assert.match(await b.evaluate('getComputedStyle(document.querySelector("#sections")).overflowX'), /^(auto|scroll)$/, `strip scrolls at ${w}px`);
|
||||
+ assert.deepEqual(await b.evaluate('[...document.querySelectorAll("#sections a")].filter(a=>a.getClientRects().length>1||a.scrollWidth>a.clientWidth+1).map(a=>a.textContent)'), [], `no wrapped label at ${w}px`);
|
||||
+ await flat();
|
||||
+ }
|
||||
|
||||
// Bus, a failed read over kept data: the kept rows stay under a stale banner; the bar says stale.
|
||||
s.rows.tasks = [{ task_ref: 'vikunja:32/7', fields: { title: 'Kept task', bucket: 2, done: false, assignees: [] }, source: 'self' }, { task_ref: 'vikunja:32/8', fields: { title: 'Second task', bucket: 2, done: false, assignees: [] }, source: 'self' }];
|
||||
await go('#/trail/task/vikunja%3A32%2F7', 'Trail for task #7');
|
||||
- assert.match(await text('#bus-view .empty'), /Nothing of this kind in the trail\./);
|
||||
+ assert.match(await text('#bus-view .empty'), /^Nothing of this kind in the trail\.Every step the stack takes on this subject writes a trail row\.$/);
|
||||
await flat(); await shot('bus-empty-trail-400');
|
||||
await go('#/tasks', 'Tasks');
|
||||
assert.equal(await b.evaluate('document.querySelectorAll("#bus-view td.s1-title a").length'), 2);
|
||||
@@ -197,7 +231,7 @@
|
||||
assert.equal(await b.evaluate('document.querySelector("#bus-view .banner.ref code").textContent'), 'human-required');
|
||||
assert.equal(await b.evaluate('document.querySelectorAll("#bus-view td.s1-title a").length'), 0);
|
||||
assert.match(await text('#bus-view .empty'), /Nothing to show\./);
|
||||
- assert.match(await text('#fresh-bus'), /not read: the read failed/);
|
||||
+ assert.equal(await text('#fresh-bus'), 'Bus not read: refused');
|
||||
await flat(); await shot('bus-refused-400');
|
||||
s.fail(null);
|
||||
|
||||
@@ -209,6 +243,8 @@
|
||||
// Ctrl+K: opens on the views and the kept tasks, filters, Enter goes, Esc restores focus.
|
||||
await go('#/inbox', 'Inbox');
|
||||
await b.evaluate('document.querySelector("#refresh").focus()');
|
||||
+ await b.key('k', 'KeyK', 3); await new Promise(r => setTimeout(r, 200));
|
||||
+ assert.equal(await b.evaluate('document.querySelector("#cmdk-dialog").open'), false, 'Ctrl+Alt+K opens nothing');
|
||||
await b.key('k', 'KeyK', 2); await wait('document.querySelector("#cmdk-dialog").open');
|
||||
assert.equal(await b.evaluate('document.activeElement.id'), 'cmdk-input');
|
||||
await wait('[...document.querySelectorAll("#cmdk-list [data-href]")].some(e=>e.dataset.href==="#/tasks/vikunja%3A32%2F7")');
|
||||
@@ -251,6 +287,8 @@
|
||||
await wait('document.querySelector("#toast").classList.contains("err")');
|
||||
assert.equal(await text('#toast span'), 'The clipboard is unavailable. The command is selected; press Ctrl+C to copy it.');
|
||||
assert.equal(await b.evaluate('getSelection().toString()'), `mosaic decide ${'d'.repeat(32)} yes`);
|
||||
+ await new Promise(r => setTimeout(r, 3000));
|
||||
+ assert.equal(await b.evaluate('document.querySelector("#toast").className'), 'toast on err', 'the error toast stays until dismissed');
|
||||
await b.evaluate('document.querySelector("#toast [data-dismiss]").click()');
|
||||
assert.equal(await b.evaluate('document.querySelector("#toast").classList.contains("on")'), false);
|
||||
|
||||
@@ -293,6 +331,7 @@
|
||||
const count = sel => b.evaluate(`document.querySelectorAll(${JSON.stringify(sel)}).length`);
|
||||
const flat = async () => assert.equal(await b.evaluate('document.documentElement.scrollWidth<=document.documentElement.clientWidth'), true, 'sideways scroll at 400px');
|
||||
const refresh = async () => { await b.evaluate('document.querySelector("#refresh").click()'); await wait('!document.querySelector("#refresh").disabled'); };
|
||||
+ const go = async (hash, h1) => { await b.evaluate(`location.hash=${JSON.stringify(hash)}`); await wait(`document.querySelector("#bus-view h1")?.textContent.startsWith(${JSON.stringify(h1)}) && !document.querySelector("#bus-view [aria-busy]")`); };
|
||||
try {
|
||||
await b.call('Page.addScriptToEvaluateOnNewDocument', { source: 'window.errors=[];addEventListener("error",e=>errors.push(e.message));addEventListener("unhandledrejection",e=>errors.push(String(e.reason)));window.requests=[];const f=window.fetch;window.fetch=(u,o={})=>{requests.push([(o.method||"GET").toUpperCase(),String(u)]);return f(u,o)};' });
|
||||
await b.viewport(400, 900);
|
||||
@@ -332,17 +371,55 @@
|
||||
assert.equal(await count(rows), n, `${hash} keeps its rows`);
|
||||
assert.match(await text('#fresh-bus'), /stale: the last read failed/, hash);
|
||||
await flat(); await shot(`${name}-stale-400`);
|
||||
- // A refusal: no kept rows, the code shown, nothing current.
|
||||
- s.fail('human-required'); await refresh();
|
||||
- await wait('!!document.querySelector("#bus-view .banner.ref")');
|
||||
- assert.equal(await text('#bus-view .banner.ref code'), 'human-required', hash);
|
||||
- assert.equal(await count(rows), 0, `${hash} hides kept rows on a refusal`);
|
||||
- assert.match(await text('#bus-view .empty'), /Nothing to show\./, hash);
|
||||
- await flat(); await shot(`${name}-refused-400`);
|
||||
- s.fail(null); await refresh();
|
||||
- await wait(`!document.querySelector("#bus-view .banner") && document.querySelectorAll(${JSON.stringify(rows)}).length===${n}`);
|
||||
+ // A refusal, by the bus (403) or for want of one (no-bus-host): no kept rows, the code
|
||||
+ // shown, nothing current, and no count in the section list.
|
||||
+ for (const code of ['human-required', 'no-bus-host']) {
|
||||
+ s.fail(code); await refresh();
|
||||
+ await wait('!!document.querySelector("#bus-view .banner.ref")');
|
||||
+ assert.equal(await text('#bus-view .banner.ref code'), code, hash);
|
||||
+ assert.equal(await count(rows), 0, `${hash} hides kept rows on a ${code} refusal`);
|
||||
+ assert.match(await text('#bus-view .empty'), /Nothing to show\./, hash);
|
||||
+ assert.equal(await text('#fresh-bus'), 'Bus not read: refused', hash);
|
||||
+ assert.equal(await count('#sections .count, #sections .tag-block'), 0, `${hash} drops the Inbox count on a ${code} refusal`);
|
||||
+ if (code === 'human-required') { await flat(); await shot(`${name}-refused-400`); }
|
||||
+ s.fail(null); await refresh();
|
||||
+ await wait(`!document.querySelector("#bus-view .banner") && document.querySelectorAll(${JSON.stringify(rows)}).length===${n}`);
|
||||
+ }
|
||||
}
|
||||
|
||||
+ // After a refusal the palette lists only the views: no kept decision or task.
|
||||
+ const views = ['#/', '#/inbox', '#/tasks', '#/agents'];
|
||||
+ const palette = '[...document.querySelectorAll("#cmdk-list [data-href]")].map(e=>e.dataset.href)';
|
||||
+ await b.evaluate('location.hash="#/inbox"'); await wait('document.querySelectorAll("#sections .count").length===1');
|
||||
+ assert.match(await text('#sections'), /Inbox 1 1 blocking/);
|
||||
+ s.fail('human-required'); await refresh(); await wait('!!document.querySelector("#bus-view .banner.ref")');
|
||||
+ await b.key('k', 'KeyK', 2); await wait('document.querySelector("#cmdk-dialog").open');
|
||||
+ assert.deepEqual(await b.evaluate(palette), views);
|
||||
+ await b.key('Escape'); await wait('!document.querySelector("#cmdk-dialog").open');
|
||||
+ s.fail(null);
|
||||
+ // The palette's own reads: kept rows show until a refused refetch, then go, with the count.
|
||||
+ await go('#/tasks', 'Tasks'); await b.evaluate('location.hash="#/inbox"'); await wait('document.querySelectorAll("#sections .count").length===1');
|
||||
+ await b.key('k', 'KeyK', 2); await wait(`${palette}.some(h=>h.startsWith("#/tasks/"))`);
|
||||
+ await b.key('Escape'); await wait('!document.querySelector("#cmdk-dialog").open');
|
||||
+ s.fail('human-required');
|
||||
+ await b.key('k', 'KeyK', 2); await wait(`document.querySelector("#cmdk-dialog").open && ${palette}.length===${views.length} && !document.querySelector("#sections .count")`);
|
||||
+ assert.deepEqual(await b.evaluate(palette), views);
|
||||
+ await b.key('Escape'); await wait('!document.querySelector("#cmdk-dialog").open');
|
||||
+ // Palette labels are bus text: markup in a question or a task title shows as text.
|
||||
+ s.fail(null);
|
||||
+ s.rows.inbox = [{ ...s.rows.inbox[0], question: '<img src=x onerror="errors.push(1)" id="pk-q">Push?' }];
|
||||
+ s.rows.tasks = [{ ...s.rows.tasks[0], fields: { ...s.rows.tasks[0].fields, title: '<b id="pk-t">Bold</b> task' } }, s.rows.tasks[1]];
|
||||
+ await b.key('k', 'KeyK', 2); await wait(`${palette}.length===${views.length + 3}`);
|
||||
+ assert.equal(await count('#cmdk-list #pk-q, #cmdk-list #pk-t, #cmdk-list img, #cmdk-list b'), 0);
|
||||
+ assert.deepEqual(await b.evaluate('[...document.querySelectorAll("#cmdk-list [data-href] span:first-child")].slice(4).map(e=>e.textContent)'), ['<img src=x onerror="errors.push(1)" id="pk-q">Push?', '#7 <b id="pk-t">Bold</b> task', '#8 Second task']);
|
||||
+ await b.key('Escape'); await wait('!document.querySelector("#cmdk-dialog").open');
|
||||
+ // The board page's Inbox read: a refusal there drops the count too.
|
||||
+ s.fail(null); await refresh(); await wait('document.querySelectorAll("#sections .count").length===1');
|
||||
+ s.fail('no-bus-host'); await b.evaluate('location.hash="#/"'); await wait('!document.querySelector("#sections .count")');
|
||||
+ await b.key('k', 'KeyK', 2); await wait('document.querySelector("#cmdk-dialog").open');
|
||||
+ assert.deepEqual(await b.evaluate(palette), views);
|
||||
+ await b.key('Escape'); s.fail(null);
|
||||
+
|
||||
assert.deepEqual(await b.evaluate('window.errors'), []);
|
||||
assert.deepEqual(await b.evaluate('window.requests.filter(([m])=>m!=="GET")'), []);
|
||||
} finally { await b.close(); await close(web); await close(brd.server); }
|
||||
@@ -0,0 +1,9 @@
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (2559.882854ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2631.856328
|
||||
@@ -0,0 +1,9 @@
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (2599.851202ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2668.022259
|
||||
@@ -0,0 +1,9 @@
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (2438.997108ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2506.864306
|
||||
@@ -0,0 +1,14 @@
|
||||
start 2026-10-10T18:21:44Z
|
||||
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-10T18:24:57Z
|
||||
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 '2d4d4e7dbc65767b1e65426ca5fadb9136a8be5b'.
|
||||
|
||||
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 '2d4d4e7dbc65767b1e65426ca5fadb9136a8be5b'.
|
||||
|
||||
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: 2558265 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/r53b/cand) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
@@ -0,0 +1,16 @@
|
||||
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
|
||||
@@ -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,118 @@
|
||||
// Darkwing row 53 round 2 probes: the round 1 probes again, then a board 403 followed by another
|
||||
// failure (P9), and a board 403 with a conversation open (P10).
|
||||
// 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, request } 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 H = await import(u('tests/history-fixture.mjs'));
|
||||
const out = [];
|
||||
const note = (k, v) => { out.push(`${k}: ${JSON.stringify(v)}`); };
|
||||
|
||||
test('probes, fake board and bus', { 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');
|
||||
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'));
|
||||
note('P2 board refused: #fresh-board', await vis('#fresh-board'));
|
||||
// P9: still no good read, now a 500. Do the rows from before the refusal come back?
|
||||
reply = { status: 500, body: { error: 'scan failed' } };
|
||||
await refresh(); await wait('document.querySelector("#error").className==="banner err"');
|
||||
note('P9 403 then 500: board-view display', await b.evaluate('getComputedStyle(document.querySelector("#board-view")).display'));
|
||||
note('P9 403 then 500: session rows in the DOM and shown', await b.evaluate('[document.querySelectorAll("table.sessions tbody tr").length, !!document.querySelector("table.sessions")?.offsetHeight]'));
|
||||
note('P9 403 then 500: #waiting', await vis('#waiting'));
|
||||
note('P9 403 then 500: #sessions', await vis('#sessions'));
|
||||
note('P9 403 then 500: projects, footer, status, fresh', [await vis('#projects'), await vis('#footer'), await vis('#status'), await vis('#fresh-board')]);
|
||||
note('P9 403 then 500: banner', await vis('#error'));
|
||||
ok(); await refresh(); await wait('document.querySelector("#error").hidden');
|
||||
note('P9b recovered: rows, footer', [await b.evaluate('document.querySelectorAll("table.sessions tbody tr").length'), await vis('#footer')]);
|
||||
// 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');
|
||||
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'));
|
||||
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]'));
|
||||
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)]'));
|
||||
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, rect', await b.evaluate('(()=>{const t=document.querySelector("#toast"),r=t.getBoundingClientRect();return [getComputedStyle(t).display,r.width,r.height,t.querySelectorAll("button").length]})()'));
|
||||
note('errors', await b.evaluate('window.errors||null'));
|
||||
} finally {
|
||||
await b.close(); await close(web); await new Promise(r => brd.close(r));
|
||||
}
|
||||
});
|
||||
|
||||
test('probe P10, a conversation open when the board refuses', { timeout: 120000 }, async () => {
|
||||
const fx = await H.historyFixture();
|
||||
const s = H.session(`${fx.sessionsDir}/2026-10-10T00-00-00-000Z_sess-1.jsonl`, fx.projectRoot);
|
||||
s.add(H.user('Secret question from before the refusal')); s.add(H.assistant('Secret answer from before the refusal'));
|
||||
// A proxy in front of the real board that can refuse every read with 403, as control-board's Host check does.
|
||||
let refuse = false;
|
||||
const up = new URL(`http://127.0.0.1:${fx.board.address().port}`);
|
||||
const proxy = createServer((req, res) => {
|
||||
if (refuse) { for (const _ of []) void _; res.writeHead(403, { 'content-type': 'application/json' }); res.end(JSON.stringify({ error: 'non-local Host refused' })); req.resume(); return; }
|
||||
const p = request({ host: up.hostname, port: up.port, path: req.url, method: req.method, headers: { ...req.headers, host: up.host } }, r => { res.writeHead(r.statusCode, r.headers); r.pipe(res); });
|
||||
req.pipe(p);
|
||||
});
|
||||
await new Promise(r => proxy.listen(0, '127.0.0.1', r));
|
||||
const web = await startServer({ port: 0, board: `http://127.0.0.1:${proxy.address().port}` });
|
||||
const b = await browser(), wait = H.waiter(b);
|
||||
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)}})()`);
|
||||
try {
|
||||
await b.viewport(1440, 900);
|
||||
await b.navigate(`http://127.0.0.1:${web.address().port}/`);
|
||||
await wait('!!document.querySelector("[data-history]")');
|
||||
await b.evaluate('document.querySelector("[data-history]").click()');
|
||||
await wait('document.querySelector("#conv-log")?.textContent.includes("Secret answer")');
|
||||
refuse = true;
|
||||
await b.evaluate('document.querySelector("#refresh").click()'); await wait('!document.querySelector("#refresh").disabled');
|
||||
await wait('document.querySelector("#error").className==="banner ref"');
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
note('P10 conversation section', await vis('#conversation'));
|
||||
note('P10 conv-log', await vis('#conv-log'));
|
||||
note('P10 conv-meta, conv-title, conv-status', [await vis('#conv-meta'), await vis('#conv-title'), await vis('#conv-status')]);
|
||||
note('P10 board-refused empty state', await vis('#board-refused'));
|
||||
note('P10 conv-no-reply', await vis('#conv-no-reply'));
|
||||
} finally {
|
||||
console.log(out.join('\n'));
|
||||
await b.close(); await close(web); await new Promise(r => proxy.close(r)); await fx.close();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
✔ probes, fake board and bus (2848.393287ms)
|
||||
P1 board refused: board-view display: "none"
|
||||
P1 board refused: inspector: {"shown":false,"text":"agent1CloseStatewaitingProjectsecretprojTaskKept fixture taskTask sourceunknownTask set bynot applicable (task is not from a registration)Active projectunknownWorkspaceunknownModelunknownRegisteredNo "}
|
||||
P2 board refused: projects tree: {"shown":true,"text":"Not read: the board refused the read."}
|
||||
P2 board refused: footer: {"shown":true,"text":"Board not read: refused"}
|
||||
P2 board refused: #status: {"shown":true,"text":"Not read: the board refused the read"}
|
||||
P2 board refused: #fresh-board: {"shown":true,"text":"Board not read: refused"}
|
||||
P9 403 then 500: board-view display: "block"
|
||||
P9 403 then 500: session rows in the DOM and shown: [2,true]
|
||||
P9 403 then 500: #waiting: {"shown":true,"text":"agent1\nwaiting\n\nsecretproj · unknown\n\nKept fixture task\n\nInput needed: kept text\n\nLast activity 2026-10-10T00:00:00Z\nHistorySeen"}
|
||||
P9 403 then 500: #sessions: {"shown":true,"text":"AGENT\tSTATE\tTASK\tACTIVE PROJECT\tWORKSPACE\tMODEL\tREGISTERED\tLAST ACTIVITY\nagent1\nsecretproj\nHistory\twaiting\tKept fixture task\tunknown\tunknown\tunknown\n\tno\t2026-10-10T00:00:00Z\nagent2\nsecretproj\nHistory\t"}
|
||||
P9 403 then 500: projects, footer, status, fresh: [{"shown":true,"text":"Not read: the board refused the read."},{"shown":true,"text":"Board not read: refused"},{"shown":true,"text":"Not read: the board refused the read"},{"shown":true,"text":"Board not read: the read failed"}]
|
||||
P9 403 then 500: banner: {"shown":true,"text":"scan failed Board: http://127.0.0.1:46259. No board data loaded. Use Refresh to try again."}
|
||||
P9b recovered: rows, footer: [2,{"shown":true,"text":"waiting 1 · working 1"}]
|
||||
P4 bus refused: section list: {"shown":true,"text":"Board\nInbox\nTasks\nAgents"}
|
||||
P3 bus refused: palette list: ["Board | view","Inbox | view","Tasks | view","Agents | view"]
|
||||
P4b board page, bus refused: section list: {"shown":true,"text":"Board\nInbox\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",0,"The bus did not answer in time. (outcome-unknown) Console shows nothing rather than a guess. Nothing was written.\n\nRead again"]
|
||||
P8 status/live nodes before any toast: [""]
|
||||
P8 toast display, rect: ["flex",1,1,0]
|
||||
errors: null
|
||||
P10 conversation section: {"shown":true,"text":"fixture conversation\n\nrepo · Secret question from before the refusal · started 2026-10-10T18:14:21.736Z\n\nSession\nSecret question from before the refusal · 2026-10-10T18:24:21.736Z\nBack to board\n\nHisto"}
|
||||
P10 conv-log: {"shown":true,"text":"USER\n2026-10-10T18:24:21.736Z\nSecret question from before the refusal\nASSISTANT\n2026-10-10T18:24:21.736Z\nSecret answer from before the refusal"}
|
||||
P10 conv-meta, conv-title, conv-status: [{"shown":true,"text":"repo · Secret question from before the refusal · started 2026-10-10T18:14:21.736Z"},{"shown":true,"text":"fixture conversation"},{"shown":true,"text":"History unavailable: non-local Host refused."}]
|
||||
P10 board-refused empty state: {"shown":true,"text":"Nothing to show.\n\nThe board refused the read, so Console shows nothing rather than a guess. The reason is above."}
|
||||
P10 conv-no-reply: {"shown":true,"text":"This session is no longer in the board scan."}
|
||||
✔ probe P10, a conversation open when the board refuses (1888.587489ms)
|
||||
ℹ tests 2
|
||||
ℹ suites 0
|
||||
ℹ pass 2
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 4949.888449
|
||||
@@ -0,0 +1,236 @@
|
||||
# Row 53, console tokens and shell restyle, round 2 review (Darkwing)
|
||||
|
||||
Issue #1542, request comment 27156, packet comment 27157, queue revs
|
||||
362-364. Base `2d4d4e7d`. Candidate manifest sha256
|
||||
`78c3aef963b7e35500c9f8afa6341057f1a6372ced70915d175e507b5fac8406`, the
|
||||
same 16 files under `packages/webui`. The packet manifest and my snapshot
|
||||
of the checkout both check 16 OK. `round2.patch` applied to my round 1
|
||||
snapshot gives the round 2 files byte for byte, so the delta is those five
|
||||
files and nothing else.
|
||||
|
||||
Verdict: **request changes**. All five of my round 1 items are fixed for
|
||||
the cases I listed, and Sage's strip test is there and works. Every suite
|
||||
is green and 35 of 37 mutants die. Two refusal gaps are left, and both
|
||||
show data from before a board refusal:
|
||||
|
||||
- A board 403 followed by any other failed read brings the refused
|
||||
session table and the "Waiting on you" card back on screen. Filbert
|
||||
found the same thing (their C4, comment 27163).
|
||||
- A board 403 while a conversation is open leaves the conversation and
|
||||
its history on screen. I should have caught this in round 1. My probes
|
||||
then never opened History, so they didn't.
|
||||
|
||||
Both fixes are a few lines each.
|
||||
|
||||
## Method
|
||||
|
||||
- Detached worktrees at `2d4d4e7d`, `cand` and `mutwt`, each with the 16
|
||||
candidate files staged and checking 16 OK. `mutwt` still checks 16 OK
|
||||
after the mutant runs (`r2/mut/manifest-after.txt`).
|
||||
- `r2/gate.sh`: the `packages/webui` suite, `build-tokens.mjs --check`,
|
||||
then every `scripts/test-*.sh` with `DOCKER_HOST=unix:///nonexistent.sock`,
|
||||
18:21:44Z to 18:24:57Z.
|
||||
- 37 mutants (`r2/mut/mutate.py`, `r2/mut/run.sh`), each against the full
|
||||
webui suite, 18:25:08Z to 19:00:50Z. M01 to M20 are round 1's, with M11
|
||||
rewritten for the strip. M21 to M37 are new and aim at the round 2 code.
|
||||
- `test-release` with Docker, finished 19:01:30Z. Then the S5 browser test
|
||||
three times on the unmutated candidate (`r2/out/s5-rep-*.txt`), see
|
||||
note 3.
|
||||
- Probes in Chromium through the candidate's `tests/browser.mjs`
|
||||
(`r2/probes/probe2.test.mjs`, output `r2/probes/probes-cand.txt`). The
|
||||
first test is my round 1 probe set again, a fake board and fake bus
|
||||
verbs, plus P9. The second, P10, runs the real control-board from the
|
||||
candidate's `tests/history-fixture.mjs` behind a small proxy that can
|
||||
answer every request with 403, the way control-board's Host check does.
|
||||
No tracker request.
|
||||
- Read the five changed files against round 1's required list, Filbert's
|
||||
comment 27143 and `IMPLEMENTING.md`.
|
||||
|
||||
Node v26.8.1, `TMPDIR=~/darkwing-scratch/r53b/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 |
|
||||
|
||||
As in round 1, I didn't run test-task with real Docker because it makes
|
||||
live model calls. Dewey's run gave 98/0. Filbert's gate also ran the other
|
||||
node packages, all green.
|
||||
|
||||
## Round 1 items
|
||||
|
||||
| Item | Result |
|
||||
|---|---|
|
||||
| Required 1, board refusal | Fixed for a single refusal. The inspector closes, the tree reads "Not read: the board refused the read.", the counts show "–", and the footer, `#status` and `#fresh-board` all say not read (P1, P2) |
|
||||
| Required 2, bus refusal | Fixed. `forget()` clears `last` and the Inbox count from a view, from the board route and from `pkOpen`. The palette lists only the four views and the section list has no counts (P3, P4, P4b). P7 now shows no task rows |
|
||||
| Required 3, tests | Done for the inspector. Not for an open conversation, see Required 2 below |
|
||||
| Required 4, M01 | Done. `no-bus-host` runs over kept rows and M01 dies |
|
||||
| Required 5, M08 | Done. Markup in a question and a task title, and M08 dies |
|
||||
| Filbert C3 | Done. "Board not read: refused" |
|
||||
|
||||
The notes: the strip is now one row below 760px (note 1). The toast stays
|
||||
in the accessibility tree while off, at 1×1 with `clip-path` (note 4, P8).
|
||||
M09 and M16 are tested and die. Note 2 (freshness hidden below 760px) and
|
||||
note 5 (Seen 403 wording) are unchanged, and that's fine.
|
||||
|
||||
## The strip test
|
||||
|
||||
Sage asked me to check it. `shell.test.mjs` sets 360px and then 400px and
|
||||
asserts four things at each width:
|
||||
|
||||
- every `#sections a` has the same `offsetTop`;
|
||||
- the list's computed `overflow-x` is `auto` or `scroll`;
|
||||
- no label wraps (one client rect, `scrollWidth` within the box);
|
||||
- the page doesn't scroll sideways.
|
||||
|
||||
All three of Sage's properties are pinned. Three mutants confirm it: M11
|
||||
(back to wrap and visible) fails "one row at 360px", M37 (wrap with
|
||||
`overflow-x:auto`) fails the same, and M36 (one row with
|
||||
`overflow-x:visible`) fails "strip scrolls at 360px". M36 is caught only
|
||||
by the computed-style check, because the fixture's labels fit at 360px
|
||||
and the page never overflows. That check is the right one to have.
|
||||
|
||||
## Probes
|
||||
|
||||
Fixture as in round 1: one board with two sessions (project `secretproj`,
|
||||
task "Kept fixture task"), one inbox decision and one task. P10 uses the
|
||||
control-board fixture's session, whose history holds "Secret question from
|
||||
before the refusal" and "Secret answer from before the refusal".
|
||||
|
||||
| Probe | Candidate |
|
||||
|---|---|
|
||||
| P1 board 403 with the inspector open | `#board-view` `display:none`, inspector hidden. Fixed |
|
||||
| P2 board 403 | tree, footer, `#status` and `#fresh-board` say not read. Fixed |
|
||||
| P3 bus `human-required` after a read | the palette lists only Board, Inbox, Tasks and Agents. Fixed |
|
||||
| P4, P4b the same refusal | no Inbox count or BLOCKING chip, on a bus view and on the board page. Fixed |
|
||||
| P6 `not-configured` after a good read | `banner ref`, no task rows |
|
||||
| P7 refusal, then `outcome-unknown` | `banner err`, no task rows |
|
||||
| P8 the toast before any copy | `display:flex`, 1×1, empty, in the tree |
|
||||
| **P9** good read, board 403, then a 500 | `#board-view` is `display:block` again with both session rows and the waiting card ("agent1 … secretproj … Kept fixture task … Input needed: kept text"). The banner says "No board data loaded", `#fresh-board` "the read failed", the footer still "refused" |
|
||||
| P9b then a good read | 2 rows, footer "waiting 1 · working 1". Recovery works |
|
||||
| **P10** History open, then every board request 403 | `#conversation` stays shown with the title, the meta line and both secret messages in `#conv-log`. Beside it, `#board-refused` says "Nothing to show. The board refused the read, so Console shows nothing rather than a guess." |
|
||||
|
||||
P9: on a 403, `error()` sets `data = null` but leaves the markup in
|
||||
`#sessions`, `#waiting` and `#seen`, and `render()` returns at once when
|
||||
`!data`. The table hides only through `body.board-refused #board-view`.
|
||||
The next `error()` that isn't a 403 removes that class and the old markup
|
||||
shows again. The README line "A board refusal drops the last scan and
|
||||
everything drawn from it" is not true after that second failure.
|
||||
|
||||
P10: `#conversation` sits outside `#board-view` (`index.html` line 17),
|
||||
so the `board-refused` rule doesn't reach it. `convForm()` runs and the
|
||||
reply form says "This session is no longer in the board scan", but the
|
||||
history read before the refusal stays on screen. Filbert's T7 predicts
|
||||
this from the code; P10 shows it with the real control-board.
|
||||
|
||||
## Mutants
|
||||
|
||||
35 of 37 killed (`r2/mut/summary.txt`). Every kill is an assertion or a
|
||||
wait timeout, not a load error.
|
||||
|
||||
| Mutant | Change | Result |
|
||||
|---|---|---|
|
||||
| M01 | `refusal()` drops `not-configured` and `no-bus-host` | killed (was survived) |
|
||||
| M02 | `error()`: `refused = false` | killed |
|
||||
| M03 | no `body.board-refused` toggle | killed |
|
||||
| M04 | the `refused:` code not in `<code>` | killed (was survived) |
|
||||
| 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 | killed (was survived) |
|
||||
| M09 | `:focus-visible` offset 0 | killed (was survived) |
|
||||
| M10 | focus outline 1px | killed |
|
||||
| M11 | strip back to wrap, `overflow-x` visible | killed (was 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` | killed (was survived) |
|
||||
| M17 | storage key changed | killed |
|
||||
| M18 | no `body.board-refused #board-view` rule | killed |
|
||||
| M19 | refusal shows kept rows | killed |
|
||||
| M20 | board refusal uses `banner err` | killed |
|
||||
| M21 | `forget()` does nothing | killed |
|
||||
| M22 | the board route's inbox read swallows a refusal | killed |
|
||||
| M23 | a view's refusal doesn't call `forget()` | killed |
|
||||
| M24 | `pkOpen` ignores a refused refetch | killed |
|
||||
| M25 | a board 403 keeps `selected` | killed |
|
||||
| M26 | `fresh()` loses the "refused" wording | killed |
|
||||
| M27 | a board 403 keeps the footer | killed |
|
||||
| M28 | a board 403 keeps `#status` | killed |
|
||||
| M29 | a board 403 keeps the project tree | killed |
|
||||
| M30 | a board 403 keeps the three heading counts | **survived** |
|
||||
| M31 | `freshBus` loses the refused state | killed |
|
||||
| M32 | `.toast:not(.on)` is `display:none` again | killed |
|
||||
| M33 | `forget()` keeps the Inbox count | killed |
|
||||
| M34 | `forget()` keeps `last` | killed |
|
||||
| M35 | board freshness in local time | killed |
|
||||
| M36 | strip one row, `overflow-x:visible` | killed |
|
||||
| M37 | strip wraps, `overflow-x:auto` | killed |
|
||||
|
||||
M07 survives as in round 1. The toast text comes from validated refs, and
|
||||
Dewey declined it with a reason. I accept that.
|
||||
|
||||
M30 survives because the three counts sit inside `#board-view`, which is
|
||||
hidden while refused. Today that makes it close to equivalent. After P9
|
||||
it isn't: the counts come back into view with the rows. Filbert's T7 asks
|
||||
for the same assertion. A P9 test that checks the counts kills it.
|
||||
|
||||
## Required
|
||||
|
||||
1. **P9, Filbert's C4.** After a board 403, a later failure that isn't a
|
||||
403 must not show the refused scan. Clear `#sessions`, `#waiting` and
|
||||
`#seen` in `error()` on a refusal, or hide `#board-view` whenever
|
||||
`data` is null. Test: a good read, a 403, then a 500, and assert no
|
||||
session row, no waiting card and the three counts not numeric.
|
||||
2. **P10.** A board 403 closes an open conversation, or hides
|
||||
`#conversation` under `body.board-refused`. Closing is cleaner,
|
||||
because the history then leaves the DOM. Test: open History, refuse
|
||||
the board, and assert `#conversation` is hidden and `#conv-log` is
|
||||
empty or hidden. `history-fixture.mjs` already gives you the
|
||||
conversation; a proxy that answers 403 is about 20 lines (mine is in
|
||||
`r2/probes/probe2.test.mjs`).
|
||||
|
||||
Then fix the README line on refusals if the wording no longer matches.
|
||||
|
||||
## Notes (not blocking)
|
||||
|
||||
1. I agree with Filbert's N4 to N7. N6 (a bus read in flight when
|
||||
`forget()` runs puts its rows back) is real by the code, and it only
|
||||
restores a read that succeeded, so it can wait.
|
||||
2. P1 hides the inspector, but its text stays in the DOM. With the
|
||||
inspector hidden that is fine; if you take the "clear on refusal"
|
||||
route for Required 1, clearing it too costs one line.
|
||||
3. In the M05 run, S5 also failed at `bus-browser.test.mjs` line 124
|
||||
("BODY" !== "H1"). M05 only changes the bus freshness words, so that
|
||||
failure isn't the mutant's doing. S5 passed in the gate, in the 36
|
||||
other mutant runs and three times alone afterwards. The cause is a
|
||||
race in `render()` that base `2d4d4e7d` has too. The 10 s refresh
|
||||
timer isn't cleared when a navigation starts. If it fires during a
|
||||
navigated read, the refresh (`navigated = false`) takes `gen`, the
|
||||
navigation's own render returns early, and nobody focuses the new
|
||||
`h1` or announces it. Under the mutant load a read took long enough
|
||||
to hit that window. Not this row's regression and not blocking.
|
||||
`clearTimeout(timer)` at the top of `render()`, or carrying
|
||||
`navigated` into the next render, would close it. Worth its own small
|
||||
row.
|
||||
|
||||
## Files
|
||||
|
||||
- `r2/candidate-manifest.sha256`: copy of Dewey's.
|
||||
- `r2/gate.sh`, `r2/out/`: suite runs, `summary.txt`,
|
||||
`test-release-docker.txt`, the S5 repeats and `r1-to-r2.diff`.
|
||||
- `r2/mut/`: mutant definitions, runner, diffs, outputs, `summary.txt` and
|
||||
the manifest check after the runs.
|
||||
- `r2/probes/`: the probe test and its output.
|
||||
Reference in New Issue
Block a user