review(row54): round 2 packet, approve (darkwing)
Comment 27191 on #1543, correction 27192. Candidate afb2ae0e. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
3db6ee2a4d4b05de6768709da0c21c6815c0d7899fb9389d659444828264275d packages/queue/README.md
|
||||
352d96c5cc02d01e5d524c1a9106985c0f0a12b7312d7de157d9bf733dead4d5 packages/queue/src/store.mjs
|
||||
b44f6f8b07cad4f35dfc92bee2c762845052b2b79f990f7cd5f590efd2b05544 packages/queue/tests/write.test.mjs
|
||||
c6880e55f5590b9801066e56b9cf3b76be633b60df4141a83d7701dc3d45af73 packages/webui/README.md
|
||||
07d2c06523dd89f82c9ea14757852b16434f7207df57d7dfcbc58425e6e75c1d packages/webui/src/cli.mjs
|
||||
5a611e2339fa9e31c489d24460f82852ca2bc892f07265ffdc5eca03b579541b packages/webui/src/public/app.js
|
||||
a640185c5ca1458135e2297d1fb1059c893a1269b0ff20cfb3044ce297588df2 packages/webui/src/public/bus.js
|
||||
4f65ab7f6a446b0ffd3c666b1ac1a726fbd5de0814a43b19f566a844e40163b0 packages/webui/src/queue-read.mjs
|
||||
95767269e5971d66acea98271a028ee6ebc1540dd17849a23bdaaa7ba29b42e8 packages/webui/src/reads.mjs
|
||||
8121915343ad5000cf66d474d9e4a994ce6c44ff1977822a067ad5ffd2fbbf29 packages/webui/src/serve.mjs
|
||||
9221692892ac56d7097128bd88dd34b76dc39861a3ff3996eb9eae999751775b packages/webui/tests/reads-fixture.mjs
|
||||
47a9931cdabfa2e164bd62e5f5ca51fbf05273211e539ef42659926274f946d9 packages/webui/tests/reads.test.mjs
|
||||
85f3bd247e6f99da717a51d71db13b65d511fffa8f0232ecbafa6c5000430c4e packages/webui/tests/shell.test.mjs
|
||||
35f437bcafc5ae6bc698581a0d722f6154b092cc633973e492aede0ea29eb8f4 packages/webui/tests/views.test.mjs
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
# Row 54 gate: the webui, queue and business node suites, build-tokens --check, then every scripts/test-*.sh with Docker unreachable.
|
||||
export TMPDIR=~/darkwing-scratch/r54b/tmp DOCKER_HOST=unix:///nonexistent.sock
|
||||
cd ~/darkwing-scratch/r54b/papply
|
||||
O=~/darkwing-scratch/r54b/r2/out
|
||||
: > $O/summary.txt
|
||||
echo "start $(date -u +%FT%TZ)" >> $O/summary.txt
|
||||
for p in webui queue business; do
|
||||
(cd packages/$p && env -u NODE_TEST_CONTEXT node --test 'tests/*.test.mjs') > $O/node-$p.txt 2>&1; e=$?
|
||||
echo "node-$p exit=$e $(grep -E '^ℹ (pass|fail)' $O/node-$p.txt | tr '\n' ' ')" >> $O/summary.txt
|
||||
done
|
||||
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,12 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index affb9b7b..68928537 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -546,7 +546,6 @@
|
||||
}
|
||||
async function render(navigated) {
|
||||
// A refresh due while this render reads would replace it: a navigation would lose its heading focus.
|
||||
- clearTimeout(timer);
|
||||
const route = routeOf(), [path, query = ''] = route.split('?'), board = path === '/' || path === '';
|
||||
const g = ++gen;
|
||||
document.body.classList.toggle('on-bus', !board);
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4208.719941ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4999.458945ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6941.091593ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (530.574978ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (357.773039ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (412.063113ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2622.679286ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (124.448417ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (531.690003ms)
|
||||
✔ serve refuses a --business value that is not a business id (221.754044ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4181.618291ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2796.913059ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1406.282887ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1097.638907ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4688.418436ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55535.146252ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.040646ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.251625ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2904.39286ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1237.190389ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2272.179443ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.808171ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (36.746687ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.358187ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (439.831534ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4708.546566ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23458.004134ms)
|
||||
✔ loopback host and board origin fail closed (8.268762ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (173.179223ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (353.514404ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2193.975153ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.46213ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.591883ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.502935ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7612.311877ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2752.109147ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5095.451456ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2541.799195ms)
|
||||
✔ the server log names no config path when the system config refuses (264.420147ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56093.660829
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5095.451456ms)
|
||||
AssertionError [ERR_ASSERTION]: the navigation cleared the due refresh
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:158: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 1,
|
||||
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 affb9b7b..dd081855 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -533,7 +533,7 @@
|
||||
function keep() {
|
||||
const a = document.activeElement, view = $('bus-view');
|
||||
return {
|
||||
- href: view.contains(a) ? a.getAttribute('href') : null, copy: view.contains(a) ? a.dataset?.copy : null, retry: view.contains(a) && a.hasAttribute?.('data-retry'), mirror: view.contains(a) ? a.dataset?.mirror : null, h1: view.contains(a) && a.tagName === 'H1',
|
||||
+ href: view.contains(a) ? a.getAttribute('href') : null, copy: view.contains(a) ? a.dataset?.copy : null, retry: view.contains(a) && a.hasAttribute?.('data-retry'), mirror: view.contains(a) ? a.dataset?.mirror : null,
|
||||
open: [...view.querySelectorAll('details[id]')].filter(d => d.open).map(d => d.id), status: $('copy-status')?.textContent || '',
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4348.500415ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4647.902087ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6919.883108ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (233.832445ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (128.399993ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (155.11505ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3385.889031ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (175.63599ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.588463ms)
|
||||
✔ serve refuses a --business value that is not a business id (244.249386ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4383.028811ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2657.145759ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1670.285092ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1325.034886ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4277.649078ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55713.008064ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (4.29812ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.324777ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2716.335734ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1245.168215ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2209.182022ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (18.425673ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (41.557382ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.799921ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (644.798398ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4338.61902ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22513.927723ms)
|
||||
✔ loopback host and board origin fail closed (10.001194ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (151.461961ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (97.613254ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2232.211928ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.53889ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.9854ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.291677ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7618.055582ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3340.784253ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4146.611347ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2837.097841ms)
|
||||
✔ the server log names no config path when the system config refuses (268.051823ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56241.840037
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4146.611347ms)
|
||||
AssertionError [ERR_ASSERTION]: #/queue keeps heading focus
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:103:14)
|
||||
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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index affb9b7b..b9e322c0 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -542,7 +542,6 @@
|
||||
if ($('copy-status')) $('copy-status').textContent = k.status;
|
||||
const el = [...$('bus-view').querySelectorAll('a[href],[data-copy],[data-retry],[data-mirror]')].find(e => k.href ? e.getAttribute('href') === k.href : k.copy ? e.dataset.copy === k.copy : k.mirror ? e.dataset.mirror === k.mirror : k.retry && e.hasAttribute('data-retry'));
|
||||
if (el && (k.href || k.copy || k.retry || k.mirror)) el.focus({ preventScroll: true });
|
||||
- else if (k.h1) $('bus-view').querySelector('h1')?.focus({ preventScroll: true });
|
||||
}
|
||||
async function render(navigated) {
|
||||
// A refresh due while this render reads would replace it: a navigation would lose its heading focus.
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5936.990333ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7571.075761ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7894.353341ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (216.658477ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (77.752692ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (144.184326ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2430.406698ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (307.499047ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (534.164096ms)
|
||||
✔ serve refuses a --business value that is not a business id (198.223277ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5930.350514ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3774.213726ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1787.027526ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1157.073495ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5617.067363ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56136.772328ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (5.300351ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.289634ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1377.189424ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (853.483066ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (5371.652955ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (11.687595ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (16.452759ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.472022ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1178.868105ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6805.535826ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23545.481285ms)
|
||||
✔ loopback host and board origin fail closed (8.588984ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (100.116446ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (70.458252ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1279.360352ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (5.324979ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (4.065382ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.995247ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (9844.945802ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2983.290704ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5952.837978ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (4044.904936ms)
|
||||
✔ the server log names no config path when the system config refuses (427.940057ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56523.79534
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5952.837978ms)
|
||||
AssertionError [ERR_ASSERTION]: #/queue keeps heading focus
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:103:14)
|
||||
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: false,
|
||||
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 d321b483..c0491091 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -191,7 +191,7 @@
|
||||
conv = { row: rowKey, id: null, branch: null, follow: null, messages: new Map(), markers: new Map(), sessionId: null, loading: true, polling: false, returnFocus: returnTo };
|
||||
selected = null; render(); showConversation(true);
|
||||
$('conv-title').textContent = r ? `${r.agent} conversation` : 'Conversation';
|
||||
- $('conv-meta').textContent = ''; $('conv-pick').replaceChildren(); $('conv-log').replaceChildren(); drawMarkers(); convForm();
|
||||
+ $('conv-meta').textContent = ''; $('conv-log').replaceChildren(); drawMarkers(); convForm();
|
||||
if (!sameRow) $('conv-reply').value = drafts.get(rowKey) || '';
|
||||
convStatus('Loading history…');
|
||||
const slash = rowKey.lastIndexOf('/'), project = r?.project ?? rowKey.slice(0, slash), agent = r?.agent ?? rowKey.slice(slash + 1);
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5192.932491ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6671.626105ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7522.884088ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (473.014164ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (312.622442ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (141.60371ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2784.611407ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (324.283405ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.269294ms)
|
||||
✔ serve refuses a --business value that is not a business id (193.287612ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5161.956155ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3405.126943ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1432.807999ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (895.937261ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5247.077192ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55551.768246ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.447375ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.095046ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2659.512292ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1561.842751ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3535.887599ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (47.817239ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (9.846016ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (69.949869ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (583.942019ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5153.659867ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22651.637657ms)
|
||||
✔ loopback host and board origin fail closed (10.121519ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (147.27203ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (191.427035ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1892.359564ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.291996ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.587158ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.404825ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4936.384145ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3870.5278ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (6653.459383ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3218.601514ms)
|
||||
✔ the server log names no config path when the system config refuses (214.3862ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56221.788424
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:96:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4936.384145ms)
|
||||
AssertionError [ERR_ASSERTION]: no stale session in the picker
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/shell.test.mjs:220: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 1,
|
||||
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 d321b483..fab77574 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -325,7 +325,6 @@
|
||||
: `<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>`;
|
||||
fresh();
|
||||
// Clearing the board or closing a conversation can remove what had focus: give it to the page.
|
||||
- if (focused && document.activeElement === document.body) $('main').focus();
|
||||
}
|
||||
function schedule() { clearTimeout(timer); if (!paused) timer = setTimeout(refresh, 10000); }
|
||||
async function refresh() {
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4577.658283ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5143.059818ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (10302.727932ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (489.782787ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (488.78588ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (247.157332ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2502.957297ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (186.141747ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (550.901946ms)
|
||||
✔ serve refuses a --business value that is not a business id (300.318644ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4482.704509ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (6914.113485ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (4181.995544ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (2066.329928ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4174.867865ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55089.109485ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.62962ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.836337ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2550.379886ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1408.237083ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (5406.357458ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (12.024609ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (106.505158ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.923792ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1720.723619ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4486.443051ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22557.045626ms)
|
||||
✔ loopback host and board origin fail closed (10.377262ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (193.217504ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (170.030744ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2074.814401ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.369496ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.685044ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.100934ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4060.138874ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (7463.012003ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5265.171038ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (8341.47856ms)
|
||||
✔ the server log names no config path when the system config refuses (268.736137ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55746.287428
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:96:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4060.138874ms)
|
||||
AssertionError [ERR_ASSERTION]: focus after a refusal closes the conversation
|
||||
|
||||
'' !== 'main'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/shell.test.mjs:231: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: '',
|
||||
expected: 'main',
|
||||
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 d321b483..0f4fec95 100644
|
||||
--- a/packages/webui/src/public/app.js
|
||||
+++ b/packages/webui/src/public/app.js
|
||||
@@ -325,7 +325,7 @@
|
||||
: `<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>`;
|
||||
fresh();
|
||||
// Clearing the board or closing a conversation can remove what had focus: give it to the page.
|
||||
- if (focused && document.activeElement === document.body) $('main').focus();
|
||||
+ if (document.activeElement === document.body) $('main').focus();
|
||||
}
|
||||
function schedule() { clearTimeout(timer); if (!paused) timer = setTimeout(refresh, 10000); }
|
||||
async function refresh() {
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3806.308805ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4245.8225ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6294.761998ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (406.016476ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (378.386294ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (326.477432ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2846.756583ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (113.125937ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.842355ms)
|
||||
✔ serve refuses a --business value that is not a business id (268.117929ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4063.036272ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3076.449968ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (7537.679878ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (2425.97848ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3369.44327ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54430.081554ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.267306ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.295121ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2667.443452ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (712.337574ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2795.520333ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.949888ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (18.704074ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (10.745951ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (394.456608ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3341.700633ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22544.014017ms)
|
||||
✔ loopback host and board origin fail closed (9.611017ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (143.896575ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (382.234843ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1985.772993ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.352973ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (4.343082ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.368439ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4040.294605ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (5716.466986ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5619.86532ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (5361.982497ms)
|
||||
✔ the server log names no config path when the system config refuses (275.51659ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55091.523946
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/shell.test.mjs:96:1
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4040.294605ms)
|
||||
AssertionError [ERR_ASSERTION]: a failure with nothing focused leaves focus alone
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/shell.test.mjs:191: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..78c9a981 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -45,7 +45,7 @@ export function redactor({ configDir = null, dataRoot = null } = {}) {
|
||||
return text => {
|
||||
let s = unquote(String(text));
|
||||
for (const [re, label] of known) s = s.replace(re, label);
|
||||
- return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
+ return s.replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
};
|
||||
}
|
||||
// Every string in a value, deep. Keys are left alone: they are fixed names or ids.
|
||||
@@ -0,0 +1,130 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3678.512067ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3903.843967ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5493.171936ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (241.195821ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (110.851718ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (51.255495ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3320.904789ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (143.122386ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.852264ms)
|
||||
✔ serve refuses a --business value that is not a business id (245.171649ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3326.712595ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2333.936177ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2111.008937ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (6577.371911ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3517.562915ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53732.788971ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.582142ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.234375ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1278.636646ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1638.868742ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (1936.391541ms)
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (155.531005ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (22.808553ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (11.21016ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (473.477386ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3637.578426ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22876.058089ms)
|
||||
✔ loopback host and board origin fail closed (7.514381ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (138.769604ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (142.393668ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2232.792122ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.298949ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.411069ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.920183ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7342.033929ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (7159.014086ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5426.433364ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1743.406062ms)
|
||||
✔ the server log names no config path when the system config refuses (212.977487ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 35
|
||||
ℹ fail 4
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54177.291313
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:21:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.582142ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'file /run/x.token, see https://git.example/a'
|
||||
- 'file <path>, see https://git.example/a'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:25:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'file /run/x.token, see https://git.example/a',
|
||||
expected: 'file <path>, see https://git.example/a',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:48:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1278.636646ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at /srv/secret/x.token for <id>; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:/srv/secret/r.json)'
|
||||
- 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:57:10)
|
||||
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: 'token at /srv/secret/x.token for <id>; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:/srv/secret/r.json)',
|
||||
expected: 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:108:1
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (155.531005ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries /srv/secret
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:60:215)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:113:3)
|
||||
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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1743.406062ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries /srv/secret
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:60:215)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:211:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:212:35)
|
||||
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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..facc6483 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -45,7 +45,7 @@ export function redactor({ configDir = null, dataRoot = null } = {}) {
|
||||
return text => {
|
||||
let s = unquote(String(text));
|
||||
for (const [re, label] of known) s = s.replace(re, label);
|
||||
- return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
+ return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>');
|
||||
};
|
||||
}
|
||||
// Every string in a value, deep. Keys are left alone: they are fixed names or ids.
|
||||
@@ -0,0 +1,112 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3931.328382ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5970.043494ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6158.274561ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (225.440041ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (78.596474ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (62.521748ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2203.730694ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (222.282006ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.410969ms)
|
||||
✔ serve refuses a --business value that is not a business id (170.152906ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3896.868906ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2744.313649ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1630.323929ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1257.682329ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4617.887488ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54082.520901ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (5.570202ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.339625ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (492.575935ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (960.020288ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3580.548201ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (12.187166ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (61.217106ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (25.172953ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (661.263159ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4945.489941ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23288.187522ms)
|
||||
✔ loopback host and board origin fail closed (8.377735ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (96.264634ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (72.787602ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1095.327563ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.261571ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.826633ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.687011ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8019.060493ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (4455.207925ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5590.236981ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1737.394462ms)
|
||||
✔ the server log names no config path when the system config refuses (212.271942ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 3
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54448.14428
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:21:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (5.570202ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'user 123456789012345678 ok'
|
||||
- 'user <id> ok'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:27:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'user 123456789012345678 ok',
|
||||
expected: 'user <id> ok',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:48:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (492.575935ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at <path> for 523456789012345678; tokens 0600 under <path> (receipt file:<path>)'
|
||||
- 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:57:10)
|
||||
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: 'token at <path> for 523456789012345678; tokens 0600 under <path> (receipt file:<path>)',
|
||||
expected: 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1737.394462ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries id 523456789012345678
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:59:38)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:211:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:212:35)
|
||||
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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..0ffd10af 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -43,7 +43,7 @@ export function redactor({ configDir = null, dataRoot = null } = {}) {
|
||||
.sort((a, b) => b[0].length - a[0].length)
|
||||
.map(([p, label]) => [new RegExp(`${escape(p.replace(/\/+$/, ''))}(?![\\w.-])`, 'g'), label]);
|
||||
return text => {
|
||||
- let s = unquote(String(text));
|
||||
+ let s = String(text);
|
||||
for (const [re, label] of known) s = s.replace(re, label);
|
||||
return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4728.007494ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4606.165471ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6811.446749ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (232.152816ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (139.964411ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (48.463107ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3445.232541ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (163.321375ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (546.022051ms)
|
||||
✔ serve refuses a --business value that is not a business id (186.72156ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4763.25719ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2505.302061ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1597.890188ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (918.025465ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4273.618933ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55641.475104ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.971457ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.34117ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2691.151603ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1195.633838ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2989.654121ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.699895ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (5.761328ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.764472ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (200.762444ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4222.822665ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23592.882024ms)
|
||||
✔ loopback host and board origin fail closed (7.353928ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (135.536145ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (155.867461ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2308.225348ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.341359ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.798919ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.558612ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7286.368903ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2919.432083ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5390.885393ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3279.683395ms)
|
||||
✔ the server log names no config path when the system config refuses (213.512902ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56203.235049
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:21:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.971457ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
true !== false
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:35:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..a0c03c51 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -44,7 +44,7 @@ export function redactor({ configDir = null, dataRoot = null } = {}) {
|
||||
.map(([p, label]) => [new RegExp(`${escape(p.replace(/\/+$/, ''))}(?![\\w.-])`, 'g'), label]);
|
||||
return text => {
|
||||
let s = unquote(String(text));
|
||||
- for (const [re, label] of known) s = s.replace(re, label);
|
||||
+
|
||||
return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4722.268365ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5400.932358ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5778.666337ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (263.664355ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (125.445744ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (43.840408ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2331.699754ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (309.594864ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (531.213591ms)
|
||||
✔ serve refuses a --business value that is not a business id (195.01894ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4727.096661ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2170.952575ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1853.995345ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (920.933734ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4725.348709ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53969.84547ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (5.237736ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.306016ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1517.775138ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (993.552147ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3562.394769ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.729812ms)
|
||||
✖ business: missing or invalid file refuses with the module's text, redacted (2.779218ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.561618ms)
|
||||
✖ settings: release, board, loopback address, notifier binding name only (339.833526ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5289.213889ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23306.13427ms)
|
||||
✔ loopback host and board origin fail closed (9.493386ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (167.027399ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (175.030721ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1121.549859ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.53641ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (6.446344ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (8.171333ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7825.244186ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2546.776066ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5798.424176ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2211.462107ms)
|
||||
✔ the server log names no config path when the system config refuses (317.455796ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 35
|
||||
ℹ fail 4
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54509.985201
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:21:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (5.237736ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'business file not found: <path>'
|
||||
- 'business file not found: <config>/businesses/a.json'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:23:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'business file not found: <path>',
|
||||
expected: 'business file not found: <config>/businesses/a.json',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:136:1
|
||||
✖ business: missing or invalid file refuses with the module's text, redacted (2.779218ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
error: 'not-configured',
|
||||
+ message: 'business file not found: <path>'
|
||||
- message: 'business file not found: <config>/businesses/acme.json'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:142:10)
|
||||
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: { error: 'not-configured', message: 'business file not found: <path>' },
|
||||
expected: { error: 'not-configured', message: 'business file not found: <config>/businesses/acme.json' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:171:1
|
||||
✖ settings: release, board, loopback address, notifier binding name only (339.833526ms)
|
||||
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /^no notifier config at <dataRoot>\/notify\/acme\/notify\.json/. Input:
|
||||
|
||||
'no notifier config at <path>; write {"notifyVersion": 1, "binding": "<discord binding>"} there, mode 0600, or "binding": null to run without DMs'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:187:10)
|
||||
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: 'no notifier config at <path>; write {"notifyVersion": 1, "binding": "<discord binding>"} there, mode 0600, or "binding": null to run without DMs',
|
||||
expected: /^no notifier config at <dataRoot>\/notify\/acme\/notify\.json/,
|
||||
operator: 'match',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2211.462107ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'business file not found: <path>'
|
||||
- 'business file not found: <config>/businesses/acme.json'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:239: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: 'business file not found: <path>',
|
||||
expected: 'business file not found: <config>/businesses/acme.json',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..84240cbf 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -181,7 +181,7 @@ export function consoleReads({ root, system = null, configDir = null, rolesDir,
|
||||
return {
|
||||
async queue() {
|
||||
const doc = await readQueue();
|
||||
- return scrub({ rows: doc.rows.map(queueSummary), notes: doc.notes, at: new Date(now()).toISOString() }, redact);
|
||||
+ return { rows: doc.rows.map(queueSummary), notes: doc.notes, at: new Date(now()).toISOString() };
|
||||
},
|
||||
async row(id) {
|
||||
const doc = await readQueue();
|
||||
@@ -0,0 +1,90 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5374.994585ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5154.547699ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7958.465663ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (324.05237ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (146.699204ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (116.658124ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2864.586946ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (748.526501ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.070455ms)
|
||||
✔ serve refuses a --business value that is not a business id (183.667224ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5124.550084ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3191.925738ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1767.818426ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1183.383133ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5331.263643ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56394.632605ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.117182ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.28685ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1422.931001ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (3404.135366ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2998.642045ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (23.923501ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (14.964285ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (14.878068ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (385.602784ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5428.031155ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23455.509921ms)
|
||||
✔ loopback host and board origin fail closed (7.720627ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (146.186748ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (148.941151ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1731.86876ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.191567ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.34193ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.864872ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7824.111254ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2729.619118ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5629.939344ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2681.240296ms)
|
||||
✔ the server log names no config path when the system config refuses (403.992828ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56903.397336
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:48:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1422.931001ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at /srv/secret/x.token for 523456789012345678; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:/srv/secret/r.json)'
|
||||
- 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:57:10)
|
||||
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: 'token at /srv/secret/x.token for 523456789012345678; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:/srv/secret/r.json)',
|
||||
expected: 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2681.240296ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries id 523456789012345678
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:59:38)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:211:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:212:35)
|
||||
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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..cb944e97 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -187,7 +187,7 @@ export function consoleReads({ root, system = null, configDir = null, rolesDir,
|
||||
const doc = await readQueue();
|
||||
const row = doc.rows.find(r => r.id === id);
|
||||
if (!row) throw fail('not-found', `no row ${id}`);
|
||||
- return scrub({ row, notes: doc.notes, at: new Date(now()).toISOString() }, redact);
|
||||
+ return { row, notes: doc.notes, at: new Date(now()).toISOString() };
|
||||
},
|
||||
async business() {
|
||||
const b = loaded();
|
||||
@@ -0,0 +1,70 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4676.256766ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5652.009835ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5982.448685ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (256.274111ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (108.264037ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (80.124769ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2178.092946ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (251.408163ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.002981ms)
|
||||
✔ serve refuses a --business value that is not a business id (199.063046ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4714.136805ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2612.545133ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1303.086032ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1378.328177ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5213.376009ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54056.852181ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.612748ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.168115ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1310.294031ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1063.140646ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3836.191496ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (11.044211ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (22.247917ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.606046ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (485.438037ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4877.471577ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24394.322748ms)
|
||||
✔ loopback host and board origin fail closed (8.420247ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (133.519218ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (105.873123ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1057.915363ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.991312ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.154849ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.422882ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8069.859073ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2618.472126ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5865.88656ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1477.079296ms)
|
||||
✔ the server log names no config path when the system config refuses (265.243078ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54456.915528
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1477.079296ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at /srv/secret/x.token for 523456789012345678; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:/srv/secret/r.json)'
|
||||
- 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:214: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: 'token at /srv/secret/x.token for 523456789012345678; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:/srv/secret/r.json)',
|
||||
expected: 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..8c7e56b1 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -153,7 +153,7 @@ export function businessView(b, { system = null, now = Date.now() } = {}) {
|
||||
for (const [service, ref] of Object.entries(b.tracker.sync.credentials)) credentials.push(credential(service, ref, b.tracker.sync.bot, 'tracker sync', now));
|
||||
return {
|
||||
id: b.id, human: b.human, arbiters: b.arbiters, projects: Object.keys(b.projects), actions: [...ACTIONS],
|
||||
- vars: pick(b.vars, VAR_KEYS), instances, launch: b.launch, credentials,
|
||||
+ vars: b.vars, instances, launch: b.launch, credentials,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4602.491827ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5159.727876ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6238.889327ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (223.687836ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (736.783592ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (201.408026ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2546.757467ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (177.799923ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (554.252001ms)
|
||||
✔ serve refuses a --business value that is not a business id (179.635775ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4698.848196ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2346.391915ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1459.063784ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (866.044357ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4545.453806ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55238.209044ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.043624ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.427885ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2428.272353ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1149.222825ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2975.803027ms)
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (8.902499ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (104.672072ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.129818ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (227.836278ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4587.181249ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22788.635144ms)
|
||||
✔ loopback host and board origin fail closed (8.672021ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (128.863615ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (81.857364ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2102.805706ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.234646ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.268908ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.85466ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7413.989444ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2836.131328ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5454.209289ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2223.529846ms)
|
||||
✔ the server log names no config path when the system config refuses (270.013578ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55720.295293
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:108:1
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (8.902499ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries discordUserId
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:60:215)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:113:3)
|
||||
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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2223.529846ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries discordUserId
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:60:215)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:211:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:229: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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..6c873bcc 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -144,7 +144,7 @@ export function businessView(b, { system = null, now = Date.now() } = {}) {
|
||||
refused = err.message;
|
||||
}
|
||||
}
|
||||
- return { instance, definition: r.definition, holder: r.holder, vars: pick(r.vars, AGENT_KEYS), authority, refused };
|
||||
+ return { instance, definition: r.definition, holder: r.holder, vars: r.vars, authority, refused };
|
||||
});
|
||||
const credentials = [];
|
||||
for (const [instance, r] of Object.entries(b.roles)) {
|
||||
@@ -0,0 +1,48 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4131.882197ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4167.204727ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6627.253569ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (283.309507ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (130.708288ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (347.918781ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3171.960558ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (118.399742ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.763455ms)
|
||||
✔ serve refuses a --business value that is not a business id (182.624836ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3919.263903ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2627.213977ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1501.84676ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1392.664804ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3997.750009ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55321.272948ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.445275ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.272509ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2609.429165ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1424.883275ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (1760.787216ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (13.683278ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (9.352988ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.205694ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (351.5801ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4078.351086ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22833.484482ms)
|
||||
✔ loopback host and board origin fail closed (8.808166ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (118.935744ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (91.85138ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2263.001801ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.284015ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.488139ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.30261ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7813.292614ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2622.207004ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5342.54415ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2567.422788ms)
|
||||
✔ the server log names no config path when the system config refuses (315.958541ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 39
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55749.29821
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..105cdbc5 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -130,7 +130,7 @@ export function credentialState(service, date, now = Date.now()) {
|
||||
}
|
||||
const credential = (service, ref, account, role, now) => {
|
||||
const dateKind = ref.expires ? 'expires' : 'rotateBy', date = ref[dateKind];
|
||||
- return { service, account, role, dateKind, date, state: credentialState(service, date, now) };
|
||||
+ return { ...ref, service, account, role, dateKind, date, state: credentialState(service, date, now) };
|
||||
};
|
||||
export function businessView(b, { system = null, now = Date.now() } = {}) {
|
||||
const instances = Object.entries(b.roles).map(([instance, r]) => {
|
||||
@@ -0,0 +1,89 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3949.132243ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4898.888391ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5716.080111ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (240.269611ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (145.45023ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (70.571976ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3037.741963ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (156.119455ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.209489ms)
|
||||
✔ serve refuses a --business value that is not a business id (183.311854ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3621.207214ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3166.097566ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1254.86862ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (996.497539ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3865.311272ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54864.923962ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.163408ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.269508ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2303.380439ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (997.87605ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2804.692817ms)
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (10.857313ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (8.456777ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (10.228548ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (699.092336ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3994.012616ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22487.710403ms)
|
||||
✔ loopback host and board origin fail closed (8.781455ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (127.524933ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (86.144701ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2070.848546ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.258834ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.241808ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.875651ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7443.85949ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2410.449774ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4986.666175ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2043.067467ms)
|
||||
✔ the server log names no config path when the system config refuses (211.499426ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55347.999572
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:108:1
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (10.857313ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries CODER_GITEA_SECRET_ENV
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:60:215)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:113:3)
|
||||
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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2043.067467ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries CODER_GITEA_SECRET_ENV
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:60:215)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:211:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:229: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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..985a43dd 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -164,7 +164,7 @@ export function businessView(b, { system = null, now = Date.now() } = {}) {
|
||||
export function consoleReads({ root, system = null, configDir = null, rolesDir, business = null, env = process.env, queueTimeoutMs = 30000, now = () => Date.now() }) {
|
||||
const dataRoot = system?.dataRoot ?? null;
|
||||
const redact = redactor({ configDir, dataRoot });
|
||||
- const fail = (code, message) => new ReadError(code, redact(message));
|
||||
+ const fail = (code, message) => new ReadError(code, message);
|
||||
const queue = queueReader({ root, env, timeoutMs: queueTimeoutMs });
|
||||
const readQueue = async () => {
|
||||
try { return await queue(); } catch (err) { throw err instanceof ReadError ? fail(err.code, err.message) : fail('read-failed', 'the queue read failed'); }
|
||||
@@ -0,0 +1,93 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5867.029886ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5912.089574ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7793.618512ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (474.232052ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (602.691653ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (140.492635ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2399.529924ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (552.492231ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (546.777911ms)
|
||||
✔ serve refuses a --business value that is not a business id (240.706993ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5771.61711ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2528.334127ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1875.502367ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (885.754778ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5981.576749ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55897.546762ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.949392ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.228941ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1995.013977ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (2487.073336ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3559.528795ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.825428ms)
|
||||
✖ business: missing or invalid file refuses with the module's text, redacted (3.687303ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (10.006011ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (203.093737ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5847.99141ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23729.650368ms)
|
||||
✔ loopback host and board origin fail closed (9.369667ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (132.50777ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (111.331344ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1845.380474ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.15701ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.072417ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.945738ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8095.937044ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2831.761142ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5852.540818ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3121.887177ms)
|
||||
✔ the server log names no config path when the system config refuses (247.687992ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56355.392968
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:136:1
|
||||
✖ business: missing or invalid file refuses with the module's text, redacted (3.687303ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
error: 'not-configured',
|
||||
+ message: 'business file not found: /home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-webui-reads-rqkZ2Q/config/businesses/acme.json'
|
||||
- message: 'business file not found: <config>/businesses/acme.json'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:142:10)
|
||||
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: { error: 'not-configured', message: 'business file not found: /home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-webui-reads-rqkZ2Q/config/businesses/acme.json' },
|
||||
expected: { error: 'not-configured', message: 'business file not found: <config>/businesses/acme.json' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3121.887177ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'business file not found: /home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-webui-reads-Ned8SO/config/businesses/acme.json'
|
||||
- 'business file not found: <config>/businesses/acme.json'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:239: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: 'business file not found: /home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-webui-reads-Ned8SO/config/businesses/acme.json',
|
||||
expected: 'business file not found: <config>/businesses/acme.json',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..e7564ce4 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -204,7 +204,7 @@ export function consoleReads({ root, system = null, configDir = null, rolesDir,
|
||||
} catch (err) {
|
||||
notifier = { refused: err instanceof ReadError ? err.code : 'not-configured', message: err.message };
|
||||
}
|
||||
- return scrub({ release, business: id, notifier, at: new Date(now()).toISOString() }, redact);
|
||||
+ return { release, business: id, notifier, at: new Date(now()).toISOString() };
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4348.365756ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4590.193253ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6917.027162ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (352.357767ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (291.913158ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (135.787056ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3014.315336ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (241.415565ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (538.008542ms)
|
||||
✔ serve refuses a --business value that is not a business id (268.507569ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4319.007368ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2913.972453ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2088.469565ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (815.168859ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4289.462964ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55144.754029ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.353572ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.28609ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2666.117459ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1543.867749ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2738.10677ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (10.904728ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (7.725911ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.517597ms)
|
||||
✖ settings: release, board, loopback address, notifier binding name only (211.789585ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4107.073039ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22742.977483ms)
|
||||
✔ loopback host and board origin fail closed (9.050178ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (138.000942ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (242.709727ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2338.489446ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.43992ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.321153ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.718531ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7565.697193ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3515.178213ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5627.006557ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3522.340426ms)
|
||||
✔ the server log names no config path when the system config refuses (213.934237ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55785.179189
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:171:1
|
||||
✖ settings: release, board, loopback address, notifier binding name only (211.789585ms)
|
||||
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /^no notifier config at <dataRoot>\/notify\/acme\/notify\.json/. Input:
|
||||
|
||||
'no notifier config at /home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-webui-reads-bueESS/data/notify/acme/notify.json; write {"notifyVersion": 1, "binding": "<discord binding>"} there, mode 0600, or "binding": null to run without DMs'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:187:10)
|
||||
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: 'no notifier config at /home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-webui-reads-bueESS/data/notify/acme/notify.json; write {"notifyVersion": 1, "binding": "<discord binding>"} there, mode 0600, or "binding": null to run without DMs',
|
||||
expected: /^no notifier config at <dataRoot>\/notify\/acme\/notify\.json/,
|
||||
operator: 'match',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/cli.mjs b/packages/webui/src/cli.mjs
|
||||
index 64b3e7b4..ccbe6780 100644
|
||||
--- a/packages/webui/src/cli.mjs
|
||||
+++ b/packages/webui/src/cli.mjs
|
||||
@@ -35,7 +35,7 @@ try {
|
||||
const { dataRoot } = system;
|
||||
bus = busReader({ dataRoot, socket: socketPath(dataRoot), business: options.business ?? null });
|
||||
busLine = `Bus: ${options.business ?? 'the running bus host\'s business'}`;
|
||||
- } catch (err) { busLine = `Bus: not configured (${redactor({ configDir: configDir() })(err.message)})`; }
|
||||
+ } catch (err) { busLine = `Bus: not configured (${err.message})`; }
|
||||
const reads = consoleReads({ root: REPO, system, configDir: configDir(), rolesDir: rolesDir(), business: options.business ?? null });
|
||||
const server = await startServer({ board: options.board, port: options.port, bus, reads });
|
||||
console.log(`Mosaic Console: http://127.0.0.1:${server.address().port}/\nBoard: ${options.board}\n${busLine}\nCtrl-C stops this server.`);
|
||||
@@ -0,0 +1,72 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4223.124473ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4436.319299ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6393.227182ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (376.933607ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (335.390414ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (96.684686ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2908.829243ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (182.361482ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.752745ms)
|
||||
✔ serve refuses a --business value that is not a business id (232.77596ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4403.822956ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2502.117814ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1516.603152ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (877.20519ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4103.492276ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55031.195869ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.166716ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.112045ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2318.460758ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1670.23949ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2250.257953ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.363064ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (10.183282ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.242805ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (201.870563ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4237.656103ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22648.333507ms)
|
||||
✔ loopback host and board origin fail closed (7.331302ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (118.392945ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (126.992258ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2157.409044ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.241189ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.391278ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.938146ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7096.450587ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3160.359737ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4979.436725ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2526.695671ms)
|
||||
✖ the server log names no config path when the system config refuses (158.788827ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55511.953513
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:251:1
|
||||
✖ the server log names no config path when the system config refuses (158.788827ms)
|
||||
AssertionError [ERR_ASSERTION]: missing: the log names the temporary directory: Mosaic Console: http://127.0.0.1:37015/
|
||||
Board: http://127.0.0.1:7331
|
||||
Bus: not configured (system config problem (mosaic-config exit 3): mosaic-config: configuration not found: /home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-webui-log-qIlWKG/cfg/config.json (run scripts/bootstrap.sh to create it))
|
||||
Ctrl-C stops this server.
|
||||
|
||||
|
||||
true !== false
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:263: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..5f6f0c4e 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -83,7 +83,7 @@ export function queueReader({ root, env = process.env, timeoutMs = 30000, maxByt
|
||||
// store's text. Anything else failed to run, and its stderr may be
|
||||
// Node's own (a path and a stack), so it is not shown; the view keeps
|
||||
// its last read.
|
||||
- if (status === 2) return done(reject, new ReadError('queue-refused', said || 'the queue refused the read'));
|
||||
+ if (status === 3) return done(reject, new ReadError('queue-refused', said || 'the queue refused the read'));
|
||||
if (status !== 0) return done(reject, new ReadError('read-failed', `the queue read failed (exit ${status})`));
|
||||
try {
|
||||
const doc = JSON.parse(Buffer.concat(out).toString('utf8'));
|
||||
@@ -0,0 +1,99 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4429.40732ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5312.273282ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5485.762034ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (275.054415ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (77.848536ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (55.357571ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2290.13066ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (443.643787ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.38196ms)
|
||||
✔ serve refuses a --business value that is not a business id (203.869284ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4480.17962ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2617.543798ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1778.415676ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1148.889093ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5122.774685ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54151.79558ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.131276ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.273848ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1573.122796ms)
|
||||
✖ queue: a refused read fails closed with the store's text, redacted (917.48889ms)
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3206.28123ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (10.424364ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (15.729521ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (40.43878ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (248.206273ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4525.101236ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23639.555408ms)
|
||||
✔ loopback host and board origin fail closed (8.863177ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (136.032222ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (149.106678ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1136.35831ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.215173ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.776312ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.544684ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7924.244182ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3020.961004ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5692.379658ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2383.936414ms)
|
||||
✔ the server log names no config path when the system config refuses (278.783402ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54586.555323
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:75:1
|
||||
✖ queue: a refused read fails closed with the store's text, redacted (917.48889ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'read-failed'
|
||||
- 'queue-refused'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:81:10)
|
||||
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: 'read-failed',
|
||||
expected: 'queue-refused',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:87:1
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3206.28123ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
Comparison {
|
||||
+ code: 'queue-refused',
|
||||
+ message: 'Error: boom\n at main (file:///srv/q/x.mjs:1:1)'
|
||||
- code: 'read-failed',
|
||||
- message: 'the queue read failed (exit 3)'
|
||||
}
|
||||
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:103:3)
|
||||
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: Error: Error: boom
|
||||
at main (file:///srv/q/x.mjs:1:1)
|
||||
at ChildProcess.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/src/reads.mjs:86:45)
|
||||
at ChildProcess.emit (node:events:514:20)
|
||||
at maybeClose (node:internal/child_process:1125:16)
|
||||
at ChildProcess._handle.onexit (node:internal/child_process:305:5),
|
||||
expected: { code: 'read-failed', message: 'the queue read failed (exit 3)' },
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..fdf4d899 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -71,7 +71,7 @@ export function queueReader({ root, env = process.env, timeoutMs = 30000, maxByt
|
||||
const timer = setTimeout(() => { child.kill('SIGKILL'); done(reject, new ReadError('read-failed', 'the queue read did not finish in time')); }, timeoutMs);
|
||||
child.stdout.on('data', chunk => {
|
||||
size += chunk.length;
|
||||
- if (size > maxBytes) { child.kill('SIGKILL'); return done(reject, new ReadError('invalid-response', 'the queue read printed too much')); }
|
||||
+ if (false) { child.kill('SIGKILL'); return done(reject, new ReadError('invalid-response', 'the queue read printed too much')); }
|
||||
out.push(chunk);
|
||||
});
|
||||
child.stderr.on('data', chunk => { if (err.length < 64) err.push(chunk); });
|
||||
@@ -0,0 +1,77 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4841.222772ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5487.098928ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6839.589268ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (609.333716ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (523.696776ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (184.726738ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2442.038169ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (163.969894ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (533.294862ms)
|
||||
✔ serve refuses a --business value that is not a business id (210.829523ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4845.392664ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2468.326383ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1509.60312ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (885.846284ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4847.73469ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55584.2969ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.155997ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.260761ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2371.383524ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1709.606179ms)
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3026.091299ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.971531ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (6.755023ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.167464ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (240.2436ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4848.12045ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23908.114018ms)
|
||||
✔ loopback host and board origin fail closed (7.05045ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (112.575009ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (218.818305ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2099.421251ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.331225ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.402939ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.888766ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7358.671695ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2880.854254ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5659.295659ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2871.077194ms)
|
||||
✔ the server log names no config path when the system config refuses (316.944553ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56302.126017
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:87:1
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3026.091299ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
Comparison {
|
||||
code: 'invalid-response',
|
||||
+ message: 'the queue read printed something that is not its JSON'
|
||||
- message: 'the queue read printed too much'
|
||||
}
|
||||
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:105:3)
|
||||
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: Error: the queue read printed something that is not its JSON
|
||||
at ChildProcess.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/src/reads.mjs:92:30)
|
||||
at ChildProcess.emit (node:events:514:20)
|
||||
at maybeClose (node:internal/child_process:1125:16)
|
||||
at ChildProcess._handle.onexit (node:internal/child_process:305:5),
|
||||
expected: { code: 'invalid-response', message: 'the queue read printed too much' },
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..9c1a3702 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -68,7 +68,7 @@ export function queueReader({ root, env = process.env, timeoutMs = 30000, maxByt
|
||||
const out = [], err = [];
|
||||
let size = 0, settled = false;
|
||||
const done = (fn, value) => { if (!settled) { settled = true; clearTimeout(timer); fn(value); } };
|
||||
- const timer = setTimeout(() => { child.kill('SIGKILL'); done(reject, new ReadError('read-failed', 'the queue read did not finish in time')); }, timeoutMs);
|
||||
+ const timer = setTimeout(() => { child.kill('SIGKILL'); done(reject, new ReadError('read-failed', 'the queue read did not finish in time')); }, timeoutMs * 1000);
|
||||
child.stdout.on('data', chunk => {
|
||||
size += chunk.length;
|
||||
if (size > maxBytes) { child.kill('SIGKILL'); return done(reject, new ReadError('invalid-response', 'the queue read printed too much')); }
|
||||
@@ -0,0 +1,54 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4047.994713ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5493.364724ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6475.060786ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (344.669139ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (526.327841ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (210.186982ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2813.384958ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (420.859817ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.301788ms)
|
||||
✔ serve refuses a --business value that is not a business id (196.932872ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4508.597913ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2381.989167ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1654.282576ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1143.875431ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3691.037414ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55027.507575ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.076111ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.183395ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2331.254237ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1310.612494ms)
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (60006.561036ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.189127ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (11.046848ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.183987ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (122.816233ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4044.671923ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23956.03821ms)
|
||||
✔ loopback host and board origin fail closed (8.915996ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (137.47571ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (165.899756ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1984.05131ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.232064ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.309421ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.854287ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7668.289939ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2565.94891ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5557.335915ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2503.584613ms)
|
||||
✔ the server log names no config path when the system config refuses (210.196887ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 1
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 306671.544654
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:87:1
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (60006.561036ms)
|
||||
'test timed out after 60000ms'
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..067fb8ab 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -114,7 +114,7 @@ export function businessId({ business = null, dataRoot = null }) {
|
||||
if (!dataRoot) throw new ReadError('not-configured', 'the Console has no system config, so it knows no business');
|
||||
let state;
|
||||
try { state = readHostState(dataRoot); } catch { throw new ReadError('no-bus-host', 'the bus host state file is unreadable'); }
|
||||
- if (!state?.live) throw new ReadError('no-bus-host', 'no bus host is running and no --business was given');
|
||||
+ if (!state) throw new ReadError('no-bus-host', 'no bus host is running and no --business was given');
|
||||
return state.business;
|
||||
}
|
||||
const VAR_KEYS = ['tracker.kind', 'tracker.baseUrl', 'tracker.reconcileMinutes', 'tracker.pollSeconds', 'gitea.baseUrl', 'limits.tools', 'limits.network', 'limits.authority'];
|
||||
@@ -0,0 +1,48 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (6798.308522ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (8171.126344ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (10339.650081ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (620.104369ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (651.908336ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (107.145963ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1856.613127ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (428.950655ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (531.241142ms)
|
||||
✔ serve refuses a --business value that is not a business id (364.365598ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7312.748843ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3310.811576ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1877.555631ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (961.142231ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6694.324745ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (59345.415131ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.049565ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.262295ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2140.76369ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (2728.342112ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (4001.802398ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.942483ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (22.480108ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.902561ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1078.874523ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6688.571398ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24397.913766ms)
|
||||
✔ loopback host and board origin fail closed (8.856782ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (154.151941ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (166.653841ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1540.812704ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.325053ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.580587ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.172395ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (9786.737883ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3176.507332ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (8068.083754ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3933.18414ms)
|
||||
✔ the server log names no config path when the system config refuses (212.185224ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 39
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 59922.92532
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/webui/src/serve.mjs b/packages/webui/src/serve.mjs
|
||||
index 8e89e3ed..96f47c8c 100644
|
||||
--- a/packages/webui/src/serve.mjs
|
||||
+++ b/packages/webui/src/serve.mjs
|
||||
@@ -117,7 +117,6 @@ export async function startServer({ host = '127.0.0.1', port = 7330, board = DEF
|
||||
return busRead(res, bus, verb, search);
|
||||
}
|
||||
if (path === '/api/queue' || path.startsWith('/api/queue/') || path === '/api/business' || path === '/api/settings') {
|
||||
- if (req.method !== 'GET') return json(res, 405, { error: 'method not allowed' });
|
||||
const a = server.address(), address = `http://${a.family === 'IPv6' ? `[${a.address}]` : a.address}:${a.port}/`;
|
||||
if (path === '/api/settings') return consoleRead(res, async () => ({ ...(reads ? await reads.settings() : { release: null, business: null, notifier: { refused: 'not-configured', message: 'the Console has no reads configured' } }), board: upstream, address }));
|
||||
if (!reads) return json(res, 503, { error: 'not-configured', message: 'the Console has no reads configured' });
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (6078.744644ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6248.49229ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8218.903911ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (424.798005ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (387.042542ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (94.136201ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2106.912183ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (475.968302ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.262334ms)
|
||||
✔ serve refuses a --business value that is not a business id (180.398907ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (6107.065921ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3770.987086ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1401.864309ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (976.349453ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7366.709912ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (57136.937351ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.056273ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.268275ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (2035.856025ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (3220.497398ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3460.203009ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (14.434854ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (138.587854ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.591409ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (985.066988ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6189.061228ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22251.182976ms)
|
||||
✔ loopback host and board origin fail closed (9.093808ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (140.317167ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (307.635041ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1198.2463ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.213659ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.236961ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.011914ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8015.283452ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2999.196799ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (7352.858604ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3556.876138ms)
|
||||
✔ the server log names no config path when the system config refuses (212.127697ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57687.620628
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:48:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (2035.856025ms)
|
||||
AssertionError [ERR_ASSERTION]: /api/queue
|
||||
|
||||
200 !== 405
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:70: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 200,
|
||||
expected: 405,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/serve.mjs b/packages/webui/src/serve.mjs
|
||||
index 8e89e3ed..869ac527 100644
|
||||
--- a/packages/webui/src/serve.mjs
|
||||
+++ b/packages/webui/src/serve.mjs
|
||||
@@ -65,7 +65,7 @@ async function busRead(res, bus, verb, search) {
|
||||
}
|
||||
// Row 54 (#1543): GET only. `reads` is consoleReads() from reads.mjs; its
|
||||
// bodies and messages arrive redacted. No route here writes or calls out.
|
||||
-const QUEUE_ID = /^[1-9]\d{0,5}$/;
|
||||
+const QUEUE_ID = /^\d+$/;
|
||||
async function consoleRead(res, read) {
|
||||
try { return json(res, 200, await read()); } catch (err) {
|
||||
const code = err instanceof ReadError ? err.code : 'invalid-response';
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (6117.413837ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6173.63552ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8145.420068ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (354.997969ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (227.284138ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (276.290406ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2222.068086ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (259.752592ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.831941ms)
|
||||
✔ serve refuses a --business value that is not a business id (175.390451ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (6115.875236ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2488.727633ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1497.659535ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1046.730484ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6092.814435ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56600.088764ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.384863ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.246035ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1764.679927ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (2651.001001ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3155.803602ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (11.352967ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (71.412614ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.01258ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (510.647046ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6077.424149ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22608.126134ms)
|
||||
✔ loopback host and board origin fail closed (8.861319ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (125.999102ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (146.116087ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1269.193812ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.222661ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.472756ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.909738ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8121.904873ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2846.789436ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (6179.127378ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2537.018211ms)
|
||||
✔ the server log names no config path when the system config refuses (315.611349ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57269.119785
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:48:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1764.679927ms)
|
||||
AssertionError [ERR_ASSERTION]: 0
|
||||
|
||||
404 !== 400
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:68:72)
|
||||
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: 404,
|
||||
expected: 400,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/webui/src/serve.mjs b/packages/webui/src/serve.mjs
|
||||
index 8e89e3ed..abb97b7f 100644
|
||||
--- a/packages/webui/src/serve.mjs
|
||||
+++ b/packages/webui/src/serve.mjs
|
||||
@@ -120,7 +120,6 @@ export async function startServer({ host = '127.0.0.1', port = 7330, board = DEF
|
||||
if (req.method !== 'GET') return json(res, 405, { error: 'method not allowed' });
|
||||
const a = server.address(), address = `http://${a.family === 'IPv6' ? `[${a.address}]` : a.address}:${a.port}/`;
|
||||
if (path === '/api/settings') return consoleRead(res, async () => ({ ...(reads ? await reads.settings() : { release: null, business: null, notifier: { refused: 'not-configured', message: 'the Console has no reads configured' } }), board: upstream, address }));
|
||||
- if (!reads) return json(res, 503, { error: 'not-configured', message: 'the Console has no reads configured' });
|
||||
if (path === '/api/business') return consoleRead(res, () => reads.business());
|
||||
if (path === '/api/queue') return consoleRead(res, () => reads.queue());
|
||||
const id = path.slice('/api/queue/'.length);
|
||||
@@ -0,0 +1,70 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3388.587899ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4596.351229ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6159.688419ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (411.84871ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (631.586809ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (217.509069ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1980.222323ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (256.172401ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (570.571732ms)
|
||||
✔ serve refuses a --business value that is not a business id (235.059389ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3284.106218ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2927.125645ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1736.782424ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1236.84932ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3291.133393ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54489.159269ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (4.297024ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.288911ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1980.168077ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1168.980195ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2582.733813ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (15.217129ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (16.961039ms)
|
||||
✖ business without --business: the live bus host, else no-bus-host; no system config: not-configured (11.200116ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (294.940147ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3299.534509ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22824.43039ms)
|
||||
✔ loopback host and board origin fail closed (8.839579ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (147.849395ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (159.676948ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1438.296619ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.255244ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.208118ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.534007ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7319.215182ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2558.453587ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5376.098322ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1946.307283ms)
|
||||
✔ the server log names no config path when the system config refuses (315.108793ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55033.303432
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:158:1
|
||||
✖ business without --business: the live bus host, else no-bus-host; no system config: not-configured (11.200116ms)
|
||||
AssertionError [ERR_ASSERTION]: /api/business
|
||||
+ actual - expected
|
||||
|
||||
+ 'invalid-response'
|
||||
- 'not-configured'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:168:78)
|
||||
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: 'invalid-response',
|
||||
expected: 'not-configured',
|
||||
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 affb9b7b..793387eb 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -492,7 +492,7 @@
|
||||
}
|
||||
const failTitle = err => err.code === 'queue-refused' ? 'Queue refused the read' : err.status === 403 ? 'Bus refused the read' : ['not-configured', 'no-bus-host'].includes(err.code) ? (err.module ? 'Nothing to read' : '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', 'queue-refused'].includes(err.code);
|
||||
+ 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.' : err.module ? 'Read again.' : 'Read again, or come back when the bus answers.')}`;
|
||||
@@ -0,0 +1,59 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4002.554062ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4162.842619ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5319.591602ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (314.624789ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (129.921819ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (56.629235ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2366.201659ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (118.480372ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (535.018141ms)
|
||||
✔ serve refuses a --business value that is not a business id (207.875102ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4066.894136ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2329.410743ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1219.74782ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1085.648807ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4159.473242ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53594.588941ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.994069ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.192593ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1566.264102ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (961.478816ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3032.175194ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.344866ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (5.715274ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.587348ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (131.221001ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4174.688574ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22716.36926ms)
|
||||
✔ loopback host and board origin fail closed (8.815227ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (124.288801ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (98.827197ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1139.666219ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.238538ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.537926ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.941914ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7588.679417ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2707.433741ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (12794.066818ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1577.254508ms)
|
||||
✔ the server log names no config path when the system config refuses (211.482879ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54020.921006
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (12794.066818ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: !!document.querySelector("#bus-view .banner.ref"): undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r54b/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/r54b/mutwt/packages/webui/tests/views.test.mjs:115:9)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index affb9b7b..8fd65f85 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -567,7 +567,7 @@
|
||||
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) + (path === '/settings' ? appearance() : ''); state = refusal(err) ? 'refused' : 'failed'; }
|
||||
+ catch { html = failedView(err); state = refusal(err) ? 'refused' : 'failed'; }
|
||||
if (refusal(err)) forget();
|
||||
}
|
||||
if (g !== gen) return;
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3870.955972ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4091.144587ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6026.972553ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (528.486744ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (167.317493ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (244.333007ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2337.901175ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (140.658241ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.775992ms)
|
||||
✔ serve refuses a --business value that is not a business id (255.955525ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3974.017912ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2147.046613ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1422.710797ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1134.889127ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3951.649384ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54624.106048ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.814711ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.255761ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2055.422421ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1349.755914ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (1628.966478ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.511695ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (11.772911ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.900573ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (814.236521ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3970.092327ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22195.766568ms)
|
||||
✔ loopback host and board origin fail closed (9.005773ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (250.888173ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (296.837336ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1332.35313ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.208859ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.37262ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.933785ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7148.577753ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2510.228313ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4046.033413ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2040.787502ms)
|
||||
✔ the server log names no config path when the system config refuses (264.995459ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55144.616812
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4046.033413ms)
|
||||
AssertionError [ERR_ASSERTION]: #/settings
|
||||
|
||||
0 !== 2
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:122:16)
|
||||
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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 2,
|
||||
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 affb9b7b..35214c6c 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -628,7 +628,7 @@
|
||||
const decisions = (last.get('inbox')?.rows || []).map(d => ({ href: `#/inbox/${enc(d.id)}`, label: first(d.question), group: `decision ${short(d.id)}` }));
|
||||
const tasks = (last.get('tasks')?.rows || []).filter(r => TASK.test(String(r.task_ref))).map(r => { const t = task(r); return { href: `#/tasks/${enc(t.ref)}`, label: `#${t.id} ${t.title ?? '(no title in the current read)'}`, group: 'task' }; });
|
||||
const rows = (last.get('api:queue')?.rows || []).filter(r => Number.isInteger(r.id)).map(r => ({ href: `#/queue/${r.id}`, label: `${r.id} ${r.piece ?? ''}`, group: `queue row, ${r.state}` }));
|
||||
- pkAll = [...VIEWS.map(([href, label]) => ({ href, label, group: 'view' })), ...decisions, ...tasks, ...rows];
|
||||
+ pkAll = [...VIEWS.map(([href, label]) => ({ href, label, group: 'view' })), ...decisions, ...tasks];
|
||||
}
|
||||
function pkFill() {
|
||||
const q = $('cmdk-input').value.trim().toLowerCase();
|
||||
@@ -0,0 +1,59 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (7226.437783ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7268.597536ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (9134.26969ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (435.493217ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (328.606071ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (83.438859ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2154.856637ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (393.111577ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (536.047412ms)
|
||||
✔ serve refuses a --business value that is not a business id (247.530328ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7290.46934ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2122.951278ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1417.584934ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1070.737679ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7280.258171ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (57926.788173ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.176569ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.25049ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1616.763465ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (3367.757488ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3521.052495ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.530299ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (6.4989ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.402443ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (144.295192ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (7354.847694ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24082.07915ms)
|
||||
✔ loopback host and board origin fail closed (9.71539ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (125.492271ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (185.824682ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1213.728439ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.199931ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.331924ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.359069ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (9313.253605ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2725.128426ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15790.63923ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1260.474326ms)
|
||||
✔ the server log names no config path when the system config refuses (212.895219ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 58326.728018
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15790.63923ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: [...document.querySelectorAll("#cmdk-list [data-href]")].map(e=>e.dataset.href).includes("#/queue/7"): undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r54b/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/r54b/mutwt/packages/webui/tests/views.test.mjs:182:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index affb9b7b..748b1c50 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -533,7 +533,7 @@
|
||||
function keep() {
|
||||
const a = document.activeElement, view = $('bus-view');
|
||||
return {
|
||||
- href: view.contains(a) ? a.getAttribute('href') : null, copy: view.contains(a) ? a.dataset?.copy : null, retry: view.contains(a) && a.hasAttribute?.('data-retry'), mirror: view.contains(a) ? a.dataset?.mirror : null, h1: view.contains(a) && a.tagName === 'H1',
|
||||
+ href: view.contains(a) ? a.getAttribute('href') : null, copy: view.contains(a) ? a.dataset?.copy : null, retry: view.contains(a) && a.hasAttribute?.('data-retry'), h1: view.contains(a) && a.tagName === 'H1',
|
||||
open: [...view.querySelectorAll('details[id]')].filter(d => d.open).map(d => d.id), status: $('copy-status')?.textContent || '',
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3422.571536ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3813.922622ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5870.051931ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (565.018459ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (602.143025ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (178.749255ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2510.979832ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (129.204442ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.924954ms)
|
||||
✔ serve refuses a --business value that is not a business id (178.019281ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3390.424329ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2766.594351ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1778.990268ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1125.156563ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3151.152306ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54476.859481ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.241969ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.285566ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2579.696675ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (664.638651ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2526.230072ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (15.023538ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (12.776304ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (9.840733ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (209.611633ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3103.769143ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22266.039413ms)
|
||||
✔ loopback host and board origin fail closed (8.741578ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (140.527887ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (263.375958ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2043.669417ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.104789ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.1368ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.917159ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7110.674581ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2606.107038ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5005.725118ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2368.416104ms)
|
||||
✔ the server log names no config path when the system config refuses (212.654379ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55004.180532
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5005.725118ms)
|
||||
AssertionError [ERR_ASSERTION]: the refresh keeps focus on the mirror select
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:198: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
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 affb9b7b..44ee3f35 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -671,7 +671,7 @@
|
||||
// Settings' appearance selects drive the command bar's, and follow them back.
|
||||
document.addEventListener('change', e => {
|
||||
const m = e.target.closest?.('#bus-view select[data-mirror]');
|
||||
- if (m) { const src = $(m.dataset.mirror); src.value = m.value; src.dispatchEvent(new Event('change')); return; }
|
||||
+ if (m) return;
|
||||
if (e.target.id === 'palette' || e.target.id === 'mode') for (const s of document.querySelectorAll(`#bus-view select[data-mirror="${e.target.id}"]`)) s.value = e.target.value;
|
||||
});
|
||||
window.addEventListener('hashchange', () => render(true));
|
||||
@@ -0,0 +1,59 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3210.039647ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5919.48126ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6338.073562ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (919.123782ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (237.581986ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (192.551367ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2136.52214ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (274.012492ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (545.39136ms)
|
||||
✔ serve refuses a --business value that is not a business id (234.164395ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3544.243672ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3513.304201ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1341.946705ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1374.935202ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3335.323949ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54903.686002ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.510434ms)
|
||||
✔ credential state from the date alone, as the bus states it (2.314449ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2299.933676ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1009.130301ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3430.84929ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.472442ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (6.70986ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.945398ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (171.65694ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3239.320367ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22558.844964ms)
|
||||
✔ loopback host and board origin fail closed (7.610786ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (139.7267ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (92.609124ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1666.631056ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.167202ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.255846ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.93167ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7995.760597ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2807.285881ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15976.377927ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1308.767678ms)
|
||||
✔ the server log names no config path when the system config refuses (210.960693ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55319.096061
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15976.377927ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: document.querySelector("#mode").value==="dark": undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r54b/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/r54b/mutwt/packages/webui/tests/views.test.mjs:192:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/queue/src/store.mjs b/packages/queue/src/store.mjs
|
||||
index 237d3525..ca6c1837 100644
|
||||
--- a/packages/queue/src/store.mjs
|
||||
+++ b/packages/queue/src/store.mjs
|
||||
@@ -815,7 +815,7 @@ export function list(opts) {
|
||||
// lead decision 83). Same unlocked read as list; writes nothing.
|
||||
export function rows(opts) {
|
||||
const r = readState(opts);
|
||||
- return { rows: rowsArray(r.state), err: r.notes, code: 0 };
|
||||
+ return { rows: rowsArray(r.state), err: [], code: 0 };
|
||||
}
|
||||
|
||||
export function show(opts, id) {
|
||||
@@ -0,0 +1,182 @@
|
||||
✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1394.333101ms)
|
||||
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1140.423108ms)
|
||||
ℹ git commit -e: index.lock free during the editor
|
||||
✔ F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update (1043.222642ms)
|
||||
ℹ git commit -e -- src.txt: index.lock held during the editor
|
||||
✔ F1: step 8 with index.lock held exits 3, and ordinary commits stay refused until the printed command runs (1048.386411ms)
|
||||
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1040.712346ms)
|
||||
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1086.167611ms)
|
||||
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1348.095069ms)
|
||||
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (979.495808ms)
|
||||
✔ F1: a shared-index change during the procedure is not committed (1110.412582ms)
|
||||
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1131.105975ms)
|
||||
✔ F1: a missing or a different hook refuses (766.438973ms)
|
||||
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1306.178912ms)
|
||||
✔ F1: the canary refuses a hook that git would not run (778.002631ms)
|
||||
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1134.54885ms)
|
||||
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (1024.291992ms)
|
||||
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (1172.518852ms)
|
||||
✔ bootstrap: the archived tests and validator run outside any repository (1010.000103ms)
|
||||
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (1041.606489ms)
|
||||
✔ general: a queue write after the snapshot is not committed (1292.026899ms)
|
||||
✔ general: a snapshot whose log does not extend the base refuses (1036.985538ms)
|
||||
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (155.146831ms)
|
||||
✔ general: environment overrides, a linked worktree and usage (629.476877ms)
|
||||
✔ general: a queue path staged before the run refuses at step 1 (699.778485ms)
|
||||
✔ general: HEAD's queue tests failing in the archive refuse (893.985863ms)
|
||||
✔ genesis document serializes deterministically and replays (5.690766ms)
|
||||
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (3.06713ms)
|
||||
✔ a tampered result, receipt or viewSha fails replay (2.373692ms)
|
||||
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.168839ms)
|
||||
✔ add: defaults for an ordinary seat, privileged extras, refusals (3.866614ms)
|
||||
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (5.484063ms)
|
||||
✔ matrix: release, review round, changes requested and waiting-on-jason (14.747406ms)
|
||||
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (8.983846ms)
|
||||
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (21.334866ms)
|
||||
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.877811ms)
|
||||
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1364.684904ms)
|
||||
✔ matrix: blocked keeps the claim and returns only to previousState (4.81789ms)
|
||||
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.761016ms)
|
||||
✔ field edits: who may change what (5.564459ms)
|
||||
✔ set issues keeps a logged narrowing of closes (N10) (2.874168ms)
|
||||
✔ text the table shows refuses \ and <, everywhere it enters (N8) (2.281257ms)
|
||||
✔ every accepted text renders to nine cells on every row (N8) (20.311867ms)
|
||||
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.441717ms)
|
||||
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.62284ms)
|
||||
✔ replay holds every op id to the caller's rule (N11) (4.080487ms)
|
||||
✔ note: owner, listed reviewer or privileged; empty clears (0.964886ms)
|
||||
✔ assign moves the claim with the owner; done clears it (2.13224ms)
|
||||
✔ render is byte-stable and escapes pipes (0.452775ms)
|
||||
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.538216ms)
|
||||
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (15.14622ms)
|
||||
✔ canonical args make a retry's identity independent of list order (0.255143ms)
|
||||
✔ manifests, headings and blob ids (0.357285ms)
|
||||
✔ the migration map: one queue-map block, exact keys (0.479229ms)
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (611.676876ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (191.123194ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.333019ms)
|
||||
✔ acquire publishes the record by link; release removes only its own lock (13.52631ms)
|
||||
✔ a kill between the temp write and the link leaves no lock (60.068353ms)
|
||||
✔ a short or failed temp write refuses and leaves no lock and no temp (12.061676ms)
|
||||
✔ a link error other than EEXIST refuses (7.528959ms)
|
||||
✔ an error after the link releases the lock: unreadable gate, failing temp stat (21.595397ms)
|
||||
✔ a release that fails on a gate path is reported, never a stack trace (P1) (35.474346ms)
|
||||
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10096.129815ms)
|
||||
✔ two concurrent unlockers: the second refuses on the gate (41.230435ms)
|
||||
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (15.701983ms)
|
||||
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (23.024055ms)
|
||||
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (28.760912ms)
|
||||
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (19.667117ms)
|
||||
✔ the same pid and start on a different boot is mismatch (0.759783ms)
|
||||
✔ a foreign host is unknown whatever the local pid says; unlock refuses (76.967929ms)
|
||||
✔ unreadable /proc: classification is unknown and acquire refuses (0.651702ms)
|
||||
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.200101ms)
|
||||
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (26.005879ms)
|
||||
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (38.06564ms)
|
||||
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (32.627128ms)
|
||||
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (58.944186ms)
|
||||
✔ the migration map validates and renders the golden genesis table (3.663208ms)
|
||||
✔ the marked QUEUE.md holds every row and parked item between its markers (1.75139ms)
|
||||
✔ map-check reports each kind of drift (4.798679ms)
|
||||
✔ a request posts once as the requester; a retry sends nothing (959.691743ms)
|
||||
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (4781.600922ms)
|
||||
✔ the pre-send checks: GET user must name the requester, under the deadline (1631.634711ms)
|
||||
✔ the lead's request refuses a token for login sage (969.770152ms)
|
||||
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (994.404807ms)
|
||||
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2477.452402ms)
|
||||
✔ a same-op retry after a kill sends nothing, even with a stale view (3473.638937ms)
|
||||
✔ a held lock at the outcome exits 3 and names what the transport said (778.547047ms)
|
||||
✔ late outcomes: after an abandon, and after a resolve with the same or another id (2851.521584ms)
|
||||
✔ resolve checks the comment: issue, markers, round, candidate and author (1706.445578ms)
|
||||
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (751.233734ms)
|
||||
✔ validateRow checks a request round's shape, which every replayed entry must keep (544.743962ms)
|
||||
✔ request, changes, a new candidate, approval: every round pinned; no review files (1803.039322ms)
|
||||
✔ a row with no reviewers opens a round that posts nothing (1260.304455ms)
|
||||
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1410.610666ms)
|
||||
✔ semantics: v1 entries replay as before; review entries need v2 (510.662268ms)
|
||||
✔ set reviewers refuses the row's owner (2026-09-28) (372.077566ms)
|
||||
✔ the owner records no verdict, even as a listed reviewer (502.160993ms)
|
||||
✔ a request comment over the length limit is not sent (590.732364ms)
|
||||
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (728.424304ms)
|
||||
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2602.294566ms)
|
||||
✔ genesis: refusals before anything is written (451.161238ms)
|
||||
✔ genesis: the map must be committed, well formed, with committed briefs and seats (677.54461ms)
|
||||
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (608.082873ms)
|
||||
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (516.991848ms)
|
||||
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (664.566655ms)
|
||||
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (814.810242ms)
|
||||
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (724.5479ms)
|
||||
✔ a retried add returns the id it first allocated, after reassignment and after done (926.400294ms)
|
||||
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (693.102178ms)
|
||||
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1314.956701ms)
|
||||
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (833.698274ms)
|
||||
✔ add, set reviewers and assign refuse the row's owner as a reviewer (561.836478ms)
|
||||
✔ the working-brief check: a changed working copy refuses the start and flags next (740.488008ms)
|
||||
✔ next: resume first, then nothing for an idle seat; needs a seat (332.061522ms)
|
||||
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (723.660043ms)
|
||||
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1178.648381ms)
|
||||
✔ a hand edit to queue.json refuses every verb, reads included (583.959579ms)
|
||||
✔ verify and render --check leave bytes and mtimes unchanged (448.949088ms)
|
||||
✔ render is byte-stable across runs and repositories (360.732181ms)
|
||||
✔ snapshot and verify --snapshot (912.444616ms)
|
||||
✔ usage errors exit 4 (597.283022ms)
|
||||
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (328.527789ms)
|
||||
✔ a rename failure: nothing visible, temp removed (186.448983ms)
|
||||
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (339.137729ms)
|
||||
✔ a directory fsync failure, then sync names the op (468.11189ms)
|
||||
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (264.12832ms)
|
||||
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (205.910147ms)
|
||||
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (288.87307ms)
|
||||
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (284.74277ms)
|
||||
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (240.77ms)
|
||||
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (228.15754ms)
|
||||
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (193.658921ms)
|
||||
✔ a view write that fails keeps the op and reports a stale view (223.76549ms)
|
||||
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (651.409888ms)
|
||||
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (696.44472ms)
|
||||
✔ SIGKILL after the witness, before the view: the stale refusal names the op (647.791881ms)
|
||||
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (630.032102ms)
|
||||
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (253.696153ms)
|
||||
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1090.94125ms)
|
||||
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (328.131904ms)
|
||||
✔ a header edit during a write: the op stands, the view write is skipped with a warning (190.651961ms)
|
||||
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (227.906033ms)
|
||||
✖ rows: every row as show prints it, in list order; writes nothing; same notes and refusals as list (289.368789ms)
|
||||
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (256.465651ms)
|
||||
✔ a writer paused before and after the witness rename: readers see a tail, then a match (240.28425ms)
|
||||
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (623.838286ms)
|
||||
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (680.193502ms)
|
||||
✔ the platform check refuses other filesystems (151.391011ms)
|
||||
✔ tmpfs passes only a test layer that allows it (N5) (262.172995ms)
|
||||
✔ unlock keeps a multi-line lock record on stdout (P3) (230.37843ms)
|
||||
ℹ tests 149
|
||||
ℹ suites 0
|
||||
ℹ pass 148
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 31778.109925
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/write.test.mjs:337:1
|
||||
✖ rows: every row as show prints it, in list order; writes nothing; same notes and refusals as list (289.368789ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'rev 1 visible, not confirmed durable'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/queue/tests/write.test.mjs:349:10)
|
||||
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: [],
|
||||
expected: [ 'rev 1 visible, not confirmed durable' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
diff --git a/packages/webui/src/queue-read.mjs b/packages/webui/src/queue-read.mjs
|
||||
index ae7e4509..b7661fbe 100644
|
||||
--- a/packages/webui/src/queue-read.mjs
|
||||
+++ b/packages/webui/src/queue-read.mjs
|
||||
@@ -8,10 +8,10 @@
|
||||
// (a half-written file in a shared checkout) prints "the queue read failed",
|
||||
// never Node's own message, which names the file:// path and a stack.
|
||||
|
||||
-let QueueError = null;
|
||||
+import { rows } from '../../queue/src/store.mjs';
|
||||
+import { QueueError as QE } from '../../queue/src/errors.mjs';
|
||||
+let QueueError = QE;
|
||||
try {
|
||||
- ({ QueueError } = await import('../../queue/src/errors.mjs'));
|
||||
- const { rows } = await import('../../queue/src/store.mjs');
|
||||
const r = rows({});
|
||||
process.stdout.write(`${JSON.stringify({ rows: r.rows, notes: r.err })}\n`);
|
||||
} catch (err) {
|
||||
@@ -0,0 +1,94 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5929.625638ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5699.448049ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (15524.47714ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (331.03666ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (491.260057ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (134.817023ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2168.983997ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (685.867467ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.134616ms)
|
||||
✔ serve refuses a --business value that is not a business id (189.017088ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5683.48261ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (10673.893349ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (3317.025941ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (2797.998251ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (6107.590317ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56545.702502ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.339896ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.093126ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1861.127948ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (3247.528564ms)
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (1114.135501ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (25.021431ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (81.651426ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (70.680331ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1708.943976ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6047.658121ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22505.129912ms)
|
||||
✔ loopback host and board origin fail closed (8.811438ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (121.234293ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (76.948391ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1433.929743ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.909606ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.781846ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.448411ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8111.181718ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (9458.274548ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (6310.538998ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (10988.847653ms)
|
||||
✔ the server log names no config path when the system config refuses (276.999784ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57033.895715
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:87:1
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (1114.135501ms)
|
||||
AssertionError [ERR_ASSERTION]: syntax error
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
1,
|
||||
'',
|
||||
+ 'file:///home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-queue-test-YLeato/repo/packages/queue/src/store.mjs:1\n' +
|
||||
+ 'export const rows = (;\n' +
|
||||
+ ' ^\n' +
|
||||
+ '\n' +
|
||||
+ "SyntaxError: Unexpected token ';'\n" +
|
||||
+ ' at compileSourceTextModule (node:internal/modules/esm/utils:355:16)\n' +
|
||||
+ ' at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:93:18)\n' +
|
||||
+ ' at #translate (node:internal/modules/esm/loader:441:20)\n' +
|
||||
+ ' at afterLoad (node:internal/modules/esm/loader:509:29)\n' +
|
||||
+ ' at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:514:12)\n' +
|
||||
+ ' at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:567:36)\n' +
|
||||
+ ' at afterResolve (node:internal/modules/esm/loader:614:52)\n' +
|
||||
+ ' at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:620:12)\n' +
|
||||
+ ' at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)\n' +
|
||||
+ ' at ModuleJob.link (node:internal/modules/esm/module_job:389:17)\n' +
|
||||
+ '\n' +
|
||||
+ 'Node.js v26.8.1\n'
|
||||
- 'the queue read failed\n'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:93:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ 1, '', "file:///home/jwoltje/darkwing-scratch/r54b/tmp/mosaic-queue-test-YLeato/repo/packages/queue/src/store.mjs:1\nexport const rows = (;\n ^\n\nSyntaxError: Unexpected token ';'\n at compileSourceTextModule (node:internal/modules/esm/utils:355:16)\n at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:93:18)\n at #translate (node:internal/modules/esm/loader:441:20)\n at afterLoad (node:internal/modules/esm/loader:509:29)\n at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:514:12)\n at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:567:36)\n at afterResolve (node:internal/modules/esm/loader:614:52)\n at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:620:12)\n at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)\n at ModuleJob.link (node:internal/modules/esm/module_job:389:17)\n\nNode.js v26.8.1\n" ],
|
||||
expected: [ 1, '', 'the queue read failed\n' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..912a6a9b 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -84,7 +84,7 @@ export function queueReader({ root, env = process.env, timeoutMs = 30000, maxByt
|
||||
// Node's own (a path and a stack), so it is not shown; the view keeps
|
||||
// its last read.
|
||||
if (status === 2) return done(reject, new ReadError('queue-refused', said || 'the queue refused the read'));
|
||||
- if (status !== 0) return done(reject, new ReadError('read-failed', `the queue read failed (exit ${status})`));
|
||||
+ if (status !== 0) return done(reject, new ReadError('read-failed', said || `the queue read failed (exit ${status})`));
|
||||
try {
|
||||
const doc = JSON.parse(Buffer.concat(out).toString('utf8'));
|
||||
if (!Array.isArray(doc?.rows) || !Array.isArray(doc?.notes)) throw new Error('shape');
|
||||
@@ -0,0 +1,73 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3588.55315ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4046.440624ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5589.92317ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (203.464906ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (103.150144ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (43.636079ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2202.707583ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (207.303537ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.807348ms)
|
||||
✔ serve refuses a --business value that is not a business id (194.562603ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3652.739114ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3249.466791ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (4787.170709ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (5267.272894ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3720.365569ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53785.995754ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.725273ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.16658ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1381.574793ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1004.294774ms)
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (767.512652ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.792885ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (21.412841ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (11.313465ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (546.80936ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3753.713619ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22913.507935ms)
|
||||
✔ loopback host and board origin fail closed (7.869447ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (116.124657ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (80.637672ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (943.877109ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.2269ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.300365ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.94455ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7645.884306ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (9076.352919ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5492.400613ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1698.839121ms)
|
||||
✔ the server log names no config path when the system config refuses (316.894407ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54173.091855
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:87:1
|
||||
✖ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (767.512652ms)
|
||||
AssertionError [ERR_ASSERTION]: syntax error
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
error: 'read-failed',
|
||||
+ message: 'the queue read failed'
|
||||
- message: 'the queue read failed (exit 1)'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:97: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { error: 'read-failed', message: 'the queue read failed' },
|
||||
expected: { error: 'read-failed', message: 'the queue read failed (exit 1)' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..29bc5404 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -45,7 +45,7 @@ export function redactor({ configDir = null, dataRoot = null } = {}) {
|
||||
return text => {
|
||||
let s = unquote(String(text));
|
||||
for (const [re, label] of known) s = s.replace(re, label);
|
||||
- return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
+ return s.replace(/(^|[\s('"`=,[{<])~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
};
|
||||
}
|
||||
// Every string in a value, deep. Keys are left alone: they are fixed names or ids.
|
||||
@@ -0,0 +1,112 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4019.360031ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4839.546754ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6424.819438ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (283.874313ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (122.450665ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (62.114728ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2433.983865ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (274.03938ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (533.32155ms)
|
||||
✔ serve refuses a --business value that is not a business id (358.202399ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3941.875952ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2994.887726ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1480.471374ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1353.158154ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3605.861871ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54345.882078ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.016447ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.358031ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (983.996112ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1050.192797ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3182.961094ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (26.579923ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (42.520817ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (10.858586ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (774.588593ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3835.422134ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23602.453142ms)
|
||||
✔ loopback host and board origin fail closed (9.875578ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (125.950636ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (109.263948ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1247.602364ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.22212ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.924466ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.180531ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8333.895629ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3340.899891ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (6078.586818ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1409.85663ms)
|
||||
✔ the server log names no config path when the system config refuses (430.661909ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 3
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54789.900189
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:21:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.016447ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'file:/x, file:///srv/y and <<path>>'
|
||||
- 'file:<path>, file:<path> and <<path>>'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:31:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'file:/x, file:///srv/y and <<path>>',
|
||||
expected: 'file:<path>, file:<path> and <<path>>',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:48:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (983.996112ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at <path> for <id>; tokens 0600 under <path> (receipt file:/srv/secret/r.json)'
|
||||
- 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:57:10)
|
||||
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: 'token at <path> for <id>; tokens 0600 under <path> (receipt file:/srv/secret/r.json)',
|
||||
expected: 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1409.85663ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries /srv/secret
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:60:215)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:211:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:212:35)
|
||||
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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..0edd6a42 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -45,7 +45,7 @@ export function redactor({ configDir = null, dataRoot = null } = {}) {
|
||||
return text => {
|
||||
let s = unquote(String(text));
|
||||
for (const [re, label] of known) s = s.replace(re, label);
|
||||
- return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
+ return s.replace(/(^|[\s('"`=,[{<]|:)~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
};
|
||||
}
|
||||
// Every string in a value, deep. Keys are left alone: they are fixed names or ids.
|
||||
@@ -0,0 +1,97 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3553.582881ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3977.564709ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5601.882229ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (297.864833ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (85.70265ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (53.800654ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2249.424172ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (179.568059ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (531.205351ms)
|
||||
✔ serve refuses a --business value that is not a business id (274.029861ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3538.23539ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3435.646129ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1363.308021ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1116.5204ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3664.879557ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53959.306817ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.946346ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.254389ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1342.577207ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (896.503012ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3423.963019ms)
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (8.516208ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (11.547891ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.732898ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (186.709019ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3764.815771ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23029.518463ms)
|
||||
✔ loopback host and board origin fail closed (9.657342ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (134.051809ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (148.699494ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1162.078506ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.966918ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.156346ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.800961ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7818.927116ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2495.433571ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5417.895834ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1997.202561ms)
|
||||
✔ the server log names no config path when the system config refuses (212.977773ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54401.902793
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:21:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.946346ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'file <path>, see https:<path>'
|
||||
- 'file <path>, see https://git.example/a'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:25:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'file <path>, see https:<path>',
|
||||
expected: 'file <path>, see https://git.example/a',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:108:1
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (8.516208ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
+ 'gitea.baseUrl': 'https:<path>',
|
||||
+ 'tracker.baseUrl': 'http:<path>',
|
||||
- 'gitea.baseUrl': 'https://git.example',
|
||||
- 'tracker.baseUrl': 'http://127.0.0.1:3456',
|
||||
'tracker.pollSeconds': 60
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:116:10)
|
||||
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: { 'tracker.baseUrl': 'http:<path>', 'tracker.pollSeconds': 60, 'gitea.baseUrl': 'https:<path>' },
|
||||
expected: { 'tracker.baseUrl': 'http://127.0.0.1:3456', 'gitea.baseUrl': 'https://git.example', 'tracker.pollSeconds': 60 },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..eed11cad 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -45,7 +45,7 @@ export function redactor({ configDir = null, dataRoot = null } = {}) {
|
||||
return text => {
|
||||
let s = unquote(String(text));
|
||||
for (const [re, label] of known) s = s.replace(re, label);
|
||||
- return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
+ return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
};
|
||||
}
|
||||
// Every string in a value, deep. Keys are left alone: they are fixed names or ids.
|
||||
@@ -0,0 +1,111 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4808.795806ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5313.73431ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6311.106107ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (265.916343ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (193.901167ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (446.225526ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2870.908832ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (176.150517ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.689869ms)
|
||||
✔ serve refuses a --business value that is not a business id (168.137447ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5038.645695ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2228.362159ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1553.540451ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (907.529289ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4802.901871ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55413.167363ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.523859ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.210027ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1815.719137ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1904.673406ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2634.985616ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.709211ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (12.363352ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.891149ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (297.175236ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4799.252088ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22599.949339ms)
|
||||
✔ loopback host and board origin fail closed (9.033996ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (125.072773ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (80.60962ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2052.047232ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.223225ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.456863ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.781237ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7257.572317ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2532.034045ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5350.251251ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1783.156571ms)
|
||||
✔ the server log names no config path when the system config refuses (213.663192ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 3
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55898.68915
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:21:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.523859ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (lead decision 74)'
|
||||
- 'tokens 0600 under <path> (lead decision 74)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:29:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (lead decision 74)',
|
||||
expected: 'tokens 0600 under <path> (lead decision 74)',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:48:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1815.719137ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at <path> for <id>; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:<path>)'
|
||||
- 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:57:10)
|
||||
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: 'token at <path> for <id>; tokens 0600 under ~/.config/mosaic-dev/secrets/mosaic-stack (receipt file:<path>)',
|
||||
expected: 'token at <path> for <id>; tokens 0600 under <path> (receipt file:<path>)',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1783.156571ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries ~/
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads-fixture.mjs:60:215)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:211:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:212:35)
|
||||
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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..28298e29 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -45,7 +45,7 @@ export function redactor({ configDir = null, dataRoot = null } = {}) {
|
||||
return text => {
|
||||
let s = unquote(String(text));
|
||||
for (const [re, label] of known) s = s.replace(re, label);
|
||||
- return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
+ return s.replace(/(^|[\s('"`=,[{]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>');
|
||||
};
|
||||
}
|
||||
// Every string in a value, deep. Keys are left alone: they are fixed names or ids.
|
||||
@@ -0,0 +1,71 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4638.324888ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5386.953954ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6636.592976ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (443.957455ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (275.542727ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (143.228196ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2685.462607ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (172.264458ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (550.322903ms)
|
||||
✔ serve refuses a --business value that is not a business id (224.809501ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4443.281837ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2331.427568ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1352.496507ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1192.056894ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4576.31433ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55584.427114ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.318217ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.405671ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2452.060634ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1515.486336ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2466.521887ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.286482ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (35.701797ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.085783ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (187.229233ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4390.176562ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23404.059717ms)
|
||||
✔ loopback host and board origin fail closed (10.052908ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (214.622458ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (346.137565ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1704.285338ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.30912ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.495504ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.121012ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7180.331898ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2865.146967ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5207.697275ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2730.201284ms)
|
||||
✔ the server log names no config path when the system config refuses (211.697645ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56086.911896
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:21:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.318217ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'file:<path>, file:<path> and </srv/z.token>'
|
||||
- 'file:<path>, file:<path> and <<path>>'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:31:10)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.start (node:internal/test_runner/test:1262:17)
|
||||
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'file:<path>, file:<path> and </srv/z.token>',
|
||||
expected: 'file:<path>, file:<path> and <<path>>',
|
||||
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 affb9b7b..888cb7c9 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -405,7 +405,7 @@
|
||||
const refused = instances.filter(i => i.refused).map(i => `<li>${txt(i.instance)}: ${txt(i.refused)}</li>`).join('');
|
||||
const credRows = creds.map(c => `<tr><td>${txt(c.service)}</td><td>${txt(c.account) || '<span class="muted">not named in the business file</span>'}</td><td>${txt(c.role)}</td><td>${txt(c.dateKind === 'expires' ? 'expires' : 'rotate by')} <time datetime="${esc(c.date)}">${txt(c.date)}</time></td><td>${badge(c.state, CRED[c.state] || 'muted')}</td></tr>`).join('');
|
||||
return `${head(`Business ${txt(b.id)}`, `${readLine()} The business file is edited where it lives; Console only reads it.`)}
|
||||
- <section class="panel" aria-labelledby="bz-about"><h2 id="bz-about">Business</h2><dl class="kv"><dt>Human</dt><dd>${txt(b.human) || none}</dd><dt>Arbiters</dt><dd>${arbiterRefs(b.arbiters)}</dd><dt>Projects</dt><dd>${names(b.projects)}</dd><dt>Launcher</dt><dd>${txt(launch.by) || none}</dd><dt>May launch</dt><dd>${names(launch.instances)}</dd><dt>At most</dt><dd>${Object.entries(launch.max || {}).map(([f, c]) => `${txt(c)} ${txt(f)}`).join(', ') || none}</dd></dl>
|
||||
+ <section class="panel" aria-labelledby="bz-about"><h2 id="bz-about">Business</h2><dl class="kv"><dt>Human</dt><dd>${txt(b.human) || none}</dd><dt>Arbiters</dt><dd>${names(b.arbiters)}</dd><dt>Projects</dt><dd>${names(b.projects)}</dd><dt>Launcher</dt><dd>${txt(launch.by) || none}</dd><dt>May launch</dt><dd>${names(launch.instances)}</dd><dt>At most</dt><dd>${Object.entries(launch.max || {}).map(([f, c]) => `${txt(c)} ${txt(f)}`).join(', ') || none}</dd></dl>
|
||||
<h3>Vars</h3>${kvRows(b.vars, 'No vars Console shows are set.')}</section>
|
||||
<section aria-labelledby="bz-roles"><h2 id="bz-roles">Role instances <span class="count">${instances.length}</span></h2>${instances.length ? `<div class="table-wrap" tabindex="0" role="region" aria-label="Role instances, scroll for all columns"><table class="s1-table s1-roles"><thead><tr>${['Instance', 'Definition', 'Holder', 'Harness'].map(h => `<th scope="col">${h}</th>`).join('')}</tr></thead><tbody>${roleRows}</tbody></table></div>` : empty('This business has no role instances.', 'An instance shows here when the business file names one under roles.')}</section>
|
||||
${instances.length ? `<section aria-labelledby="bz-auth"><h2 id="bz-auth">Authority</h2><p class="small muted">Within the role, cross-role (needs the other role), or gated (needs the human), as the business package classifies each action.</p>${matrix}${refused ? `<ul class="s1-plain">${refused}</ul>` : ''}</section>` : ''}
|
||||
@@ -0,0 +1,90 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4077.410808ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4171.605361ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6360.165363ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (234.632892ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (324.526034ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (97.147284ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3296.610593ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (200.071063ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (531.239533ms)
|
||||
✔ serve refuses a --business value that is not a business id (248.942733ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3687.313618ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2833.201272ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1362.955296ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1312.956871ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3962.94978ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54936.686459ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.076275ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.246157ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2372.18889ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1236.786021ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (1913.957919ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (10.401295ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (20.742938ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (11.287289ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (660.976777ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3912.86125ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23172.185603ms)
|
||||
✔ loopback host and board origin fail closed (9.030326ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (127.524467ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (85.092345ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2133.302061ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.187824ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.309728ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.878168ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7525.436297ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2554.399927ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4568.702371ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2279.896343ms)
|
||||
✔ the server log names no config path when the system config refuses (211.912045ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55335.294053
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4568.702371ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'none'
|
||||
- 'pm (delivery)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:169: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: 'none',
|
||||
expected: 'pm (delivery)',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2279.896343ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'none'
|
||||
- 'pm (delivery), cto (technical)'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:228: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: 'pm (delivery), cto (technical)',
|
||||
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 affb9b7b..30b7b327 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -381,7 +381,7 @@
|
||||
const cmd = `scripts/mosaic queue move ${id} <state> --op <op> --by <seat>`;
|
||||
return `${head(`Row ${esc(id)}: ${txt(r.piece)}`, `${qState(r.state)}${r.previousState ? ` <span class="small muted">was ${txt(r.previousState)}</span>` : ''} ${readLine()}`)}
|
||||
${storeNotes(doc.notes)}
|
||||
- <section class="panel" aria-labelledby="q-row"><h2 id="q-row">Row</h2><dl class="kv"><dt>Owner</dt><dd>${txt(r.owner) || none}</dd><dt>Reviewers</dt><dd>${names(r.reviewers)}</dd><dt>Gate</dt><dd>${txt(r.gateOwner) || none}</dd><dt>Issues</dt><dd>${issueRefs(r.issues)}</dd><dt>Closes</dt><dd>${issueRefs(r.closes)}</dd><dt>Brief</dt><dd>${briefRef(r.brief)}${r.brief?.blob ? ` <span class="source">blob ${txt(String(r.brief.blob).slice(0, 12))}</span>` : ''}</dd><dt>After</dt><dd>${Array.isArray(r.after) && r.after.length ? r.after.map(afterRef).join(', ') : none}</dd><dt>Claim</dt><dd>${r.claim ? `${txt(r.claim.seat)} <span class="source">${txt(r.claim.op)}</span>` : none}</dd>${r.required ? `<dt>Required</dt><dd>yes${Number.isFinite(Date.parse(r.requiredSince)) ? ` since ${when(r.requiredSince)}` : ''}</dd>` : ''}${r.blockedReason ? `<dt>Blocked</dt><dd>${txt(r.blockedReason)}</dd>` : ''}${r.note ? `<dt>Note</dt><dd class="s1-q">${txt(r.note)}</dd>` : ''}<dt>Created</dt><dd>${when(r.createdAt)}</dd><dt>Updated</dt><dd>${when(r.updatedAt)}${r.updatedBy ? ` by ${txt(r.updatedBy)}` : ''}</dd></dl></section>
|
||||
+ <section class="panel" aria-labelledby="q-row"><h2 id="q-row">Row</h2><dl class="kv"><dt>Owner</dt><dd>${txt(r.owner) || none}</dd><dt>Reviewers</dt><dd>${names(r.reviewers)}</dd><dt>Gate</dt><dd>${txt(r.gateOwner) || none}</dd><dt>Issues</dt><dd>${issueRefs(r.issues)}</dd><dt>Closes</dt><dd>${issueRefs(r.closes)}</dd><dt>Brief</dt><dd>${briefRef(r.brief)}${r.brief?.blob ? ` <span class="source">blob ${txt(String(r.brief.blob).slice(0, 12))}</span>` : ''}</dd><dt>After</dt><dd>${Array.isArray(r.after) && r.after.length ? r.after.map(qLink).join(', ') : none}</dd><dt>Claim</dt><dd>${r.claim ? `${txt(r.claim.seat)} <span class="source">${txt(r.claim.op)}</span>` : none}</dd>${r.required ? `<dt>Required</dt><dd>yes${Number.isFinite(Date.parse(r.requiredSince)) ? ` since ${when(r.requiredSince)}` : ''}</dd>` : ''}${r.blockedReason ? `<dt>Blocked</dt><dd>${txt(r.blockedReason)}</dd>` : ''}${r.note ? `<dt>Note</dt><dd class="s1-q">${txt(r.note)}</dd>` : ''}<dt>Created</dt><dd>${when(r.createdAt)}</dd><dt>Updated</dt><dd>${when(r.updatedAt)}${r.updatedBy ? ` by ${txt(r.updatedBy)}` : ''}</dd></dl></section>
|
||||
<section aria-labelledby="q-review"><h2 id="q-review">Review <span class="count">${rounds.length}</span></h2>${rounds.length ? `<ol class="s1-plain">${rounds.map(round).join('')}</ol>` : '<p class="muted">No review round yet.</p>'}</section>
|
||||
<section aria-labelledby="q-move"><h2 id="q-move">Moving this row</h2><p class="small muted">${moveHelp} Copy only puts the command on your clipboard; fill in the state, op and seat before you run it.</p>
|
||||
<div class="s1-cmd"><code>${txt(cmd)}</code><button type="button" class="btn" data-copy="${esc(cmd)}" aria-label="Copy the move command for row ${esc(id)}">Copy</button></div><p class="small muted" id="copy-status"></p>
|
||||
@@ -0,0 +1,70 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3856.905512ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5475.36895ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6216.718835ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (241.895166ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (106.747958ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (85.962143ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3107.790588ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (159.018046ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.234183ms)
|
||||
✔ serve refuses a --business value that is not a business id (171.222193ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4726.634398ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2268.5771ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1393.091356ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (797.282953ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3939.317078ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54882.062425ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.218807ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.306361ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2201.684503ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1295.583176ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3057.835948ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.49504ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (7.216359ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.005679ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (302.327221ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3821.604474ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23144.699461ms)
|
||||
✔ loopback host and board origin fail closed (7.914685ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (90.078298ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (73.46242ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2125.549948ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.061105ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.238646ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.570488ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7221.583217ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2976.639377ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5499.676861ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1490.270162ms)
|
||||
✔ the server log names no config path when the system config refuses (316.204069ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55315.952746
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1490.270162ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ '[object Object]'
|
||||
- '<a href="#/queue/6">6</a> settled'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:219: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: '[object Object]',
|
||||
expected: '<a href="#/queue/6">6</a> settled',
|
||||
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 affb9b7b..069e36d1 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -381,7 +381,7 @@
|
||||
const cmd = `scripts/mosaic queue move ${id} <state> --op <op> --by <seat>`;
|
||||
return `${head(`Row ${esc(id)}: ${txt(r.piece)}`, `${qState(r.state)}${r.previousState ? ` <span class="small muted">was ${txt(r.previousState)}</span>` : ''} ${readLine()}`)}
|
||||
${storeNotes(doc.notes)}
|
||||
- <section class="panel" aria-labelledby="q-row"><h2 id="q-row">Row</h2><dl class="kv"><dt>Owner</dt><dd>${txt(r.owner) || none}</dd><dt>Reviewers</dt><dd>${names(r.reviewers)}</dd><dt>Gate</dt><dd>${txt(r.gateOwner) || none}</dd><dt>Issues</dt><dd>${issueRefs(r.issues)}</dd><dt>Closes</dt><dd>${issueRefs(r.closes)}</dd><dt>Brief</dt><dd>${briefRef(r.brief)}${r.brief?.blob ? ` <span class="source">blob ${txt(String(r.brief.blob).slice(0, 12))}</span>` : ''}</dd><dt>After</dt><dd>${Array.isArray(r.after) && r.after.length ? r.after.map(afterRef).join(', ') : none}</dd><dt>Claim</dt><dd>${r.claim ? `${txt(r.claim.seat)} <span class="source">${txt(r.claim.op)}</span>` : none}</dd>${r.required ? `<dt>Required</dt><dd>yes${Number.isFinite(Date.parse(r.requiredSince)) ? ` since ${when(r.requiredSince)}` : ''}</dd>` : ''}${r.blockedReason ? `<dt>Blocked</dt><dd>${txt(r.blockedReason)}</dd>` : ''}${r.note ? `<dt>Note</dt><dd class="s1-q">${txt(r.note)}</dd>` : ''}<dt>Created</dt><dd>${when(r.createdAt)}</dd><dt>Updated</dt><dd>${when(r.updatedAt)}${r.updatedBy ? ` by ${txt(r.updatedBy)}` : ''}</dd></dl></section>
|
||||
+ <section class="panel" aria-labelledby="q-row"><h2 id="q-row">Row</h2><dl class="kv"><dt>Owner</dt><dd>${txt(r.owner) || none}</dd><dt>Reviewers</dt><dd>${names(r.reviewers)}</dd><dt>Gate</dt><dd>${txt(r.gateOwner) || none}</dd><dt>Issues</dt><dd>${issueRefs(r.issues)}</dd><dt>Closes</dt><dd>${issueRefs(r.closes)}</dd><dt>Brief</dt><dd>${briefRef(r.brief)}${r.brief?.blob ? ` <span class="source">blob ${txt(String(r.brief.blob).slice(0, 12))}</span>` : ''}</dd><dt>After</dt><dd>${Array.isArray(r.after) && r.after.length ? r.after.map(afterRef).join(', ') : none}</dd><dt>Claim</dt><dd>${r.claim ? `${txt(r.claim.seat)} <span class="source">${txt(r.claim.op)}</span>` : none}</dd>${r.required ? `<dt>Required</dt><dd>${txt(r.required)}${Number.isFinite(Date.parse(r.requiredSince)) ? ` since ${when(r.requiredSince)}` : ''}</dd>` : ''}${r.blockedReason ? `<dt>Blocked</dt><dd>${txt(r.blockedReason)}</dd>` : ''}${r.note ? `<dt>Note</dt><dd class="s1-q">${txt(r.note)}</dd>` : ''}<dt>Created</dt><dd>${when(r.createdAt)}</dd><dt>Updated</dt><dd>${when(r.updatedAt)}${r.updatedBy ? ` by ${txt(r.updatedBy)}` : ''}</dd></dl></section>
|
||||
<section aria-labelledby="q-review"><h2 id="q-review">Review <span class="count">${rounds.length}</span></h2>${rounds.length ? `<ol class="s1-plain">${rounds.map(round).join('')}</ol>` : '<p class="muted">No review round yet.</p>'}</section>
|
||||
<section aria-labelledby="q-move"><h2 id="q-move">Moving this row</h2><p class="small muted">${moveHelp} Copy only puts the command on your clipboard; fill in the state, op and seat before you run it.</p>
|
||||
<div class="s1-cmd"><code>${txt(cmd)}</code><button type="button" class="btn" data-copy="${esc(cmd)}" aria-label="Copy the move command for row ${esc(id)}">Copy</button></div><p class="small muted" id="copy-status"></p>
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (2906.740481ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4973.666396ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5473.105297ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (334.048897ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (327.079288ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (234.902697ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2692.782254ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (189.349222ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.782334ms)
|
||||
✔ serve refuses a --business value that is not a business id (200.451627ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3004.05947ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2660.211565ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1503.749625ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1233.425272ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2916.774968ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54091.536195ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.033947ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.28147ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2484.989631ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (561.286331ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2528.710274ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (16.345176ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (15.327688ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (11.881195ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (489.625053ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2960.184824ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22990.114665ms)
|
||||
✔ loopback host and board origin fail closed (8.787566ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (129.900878ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (151.946301ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1974.607268ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.198224ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.409671ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.934324ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7112.046424ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2802.647181ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4934.634698ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1622.333656ms)
|
||||
✔ the server log names no config path when the system config refuses (213.419832ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54795.319769
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1622.333656ms)
|
||||
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /^yes( since |$)/. Input:
|
||||
|
||||
'true'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:220: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: /^yes( since |$)/,
|
||||
operator: 'match',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index affb9b7b..50ed0bdf 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -381,7 +381,7 @@
|
||||
const cmd = `scripts/mosaic queue move ${id} <state> --op <op> --by <seat>`;
|
||||
return `${head(`Row ${esc(id)}: ${txt(r.piece)}`, `${qState(r.state)}${r.previousState ? ` <span class="small muted">was ${txt(r.previousState)}</span>` : ''} ${readLine()}`)}
|
||||
${storeNotes(doc.notes)}
|
||||
- <section class="panel" aria-labelledby="q-row"><h2 id="q-row">Row</h2><dl class="kv"><dt>Owner</dt><dd>${txt(r.owner) || none}</dd><dt>Reviewers</dt><dd>${names(r.reviewers)}</dd><dt>Gate</dt><dd>${txt(r.gateOwner) || none}</dd><dt>Issues</dt><dd>${issueRefs(r.issues)}</dd><dt>Closes</dt><dd>${issueRefs(r.closes)}</dd><dt>Brief</dt><dd>${briefRef(r.brief)}${r.brief?.blob ? ` <span class="source">blob ${txt(String(r.brief.blob).slice(0, 12))}</span>` : ''}</dd><dt>After</dt><dd>${Array.isArray(r.after) && r.after.length ? r.after.map(afterRef).join(', ') : none}</dd><dt>Claim</dt><dd>${r.claim ? `${txt(r.claim.seat)} <span class="source">${txt(r.claim.op)}</span>` : none}</dd>${r.required ? `<dt>Required</dt><dd>yes${Number.isFinite(Date.parse(r.requiredSince)) ? ` since ${when(r.requiredSince)}` : ''}</dd>` : ''}${r.blockedReason ? `<dt>Blocked</dt><dd>${txt(r.blockedReason)}</dd>` : ''}${r.note ? `<dt>Note</dt><dd class="s1-q">${txt(r.note)}</dd>` : ''}<dt>Created</dt><dd>${when(r.createdAt)}</dd><dt>Updated</dt><dd>${when(r.updatedAt)}${r.updatedBy ? ` by ${txt(r.updatedBy)}` : ''}</dd></dl></section>
|
||||
+ <section class="panel" aria-labelledby="q-row"><h2 id="q-row">Row</h2><dl class="kv"><dt>Owner</dt><dd>${txt(r.owner) || none}</dd><dt>Reviewers</dt><dd>${names(r.reviewers)}</dd><dt>Gate</dt><dd>${txt(r.gateOwner) || none}</dd><dt>Issues</dt><dd>${issueRefs(r.issues)}</dd><dt>Closes</dt><dd>${issueRefs(r.closes)}</dd><dt>Brief</dt><dd>${briefRef(r.brief)}${r.brief?.blob ? ` <span class="source">blob ${txt(String(r.brief.blob).slice(0, 12))}</span>` : ''}</dd><dt>After</dt><dd>${Array.isArray(r.after) && r.after.length ? r.after.map(afterRef).join(', ') : none}</dd><dt>Claim</dt><dd>${r.claim ? `${txt(r.claim.seat)} <span class="source">${txt(r.claim.op)}</span>` : none}</dd>${r.required ? `<dt>Required</dt><dd>yes${r.requiredSince ? ` since ${when(r.requiredSince)}` : ''}</dd>` : ''}${r.blockedReason ? `<dt>Blocked</dt><dd>${txt(r.blockedReason)}</dd>` : ''}${r.note ? `<dt>Note</dt><dd class="s1-q">${txt(r.note)}</dd>` : ''}<dt>Created</dt><dd>${when(r.createdAt)}</dd><dt>Updated</dt><dd>${when(r.updatedAt)}${r.updatedBy ? ` by ${txt(r.updatedBy)}` : ''}</dd></dl></section>
|
||||
<section aria-labelledby="q-review"><h2 id="q-review">Review <span class="count">${rounds.length}</span></h2>${rounds.length ? `<ol class="s1-plain">${rounds.map(round).join('')}</ol>` : '<p class="muted">No review round yet.</p>'}</section>
|
||||
<section aria-labelledby="q-move"><h2 id="q-move">Moving this row</h2><p class="small muted">${moveHelp} Copy only puts the command on your clipboard; fill in the state, op and seat before you run it.</p>
|
||||
<div class="s1-cmd"><code>${txt(cmd)}</code><button type="button" class="btn" data-copy="${esc(cmd)}" aria-label="Copy the move command for row ${esc(id)}">Copy</button></div><p class="small muted" id="copy-status"></p>
|
||||
@@ -0,0 +1,48 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3616.980106ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3660.989138ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5949.529873ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (781.695493ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (336.486963ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (167.515827ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2477.139016ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (218.463943ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (558.942622ms)
|
||||
✔ serve refuses a --business value that is not a business id (194.435163ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3685.671044ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2898.806207ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1408.156807ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1278.383289ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3465.827892ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54715.651325ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.906711ms)
|
||||
✔ credential state from the date alone, as the bus states it (2.48486ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2492.569641ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1078.093433ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2515.949704ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.841988ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (143.951841ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.545556ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (178.175074ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3562.830873ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22389.648835ms)
|
||||
✔ loopback host and board origin fail closed (9.000085ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (151.76069ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (413.917858ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1882.086271ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.293433ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.230358ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.561316ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7591.926402ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2569.169226ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5271.25471ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2374.300294ms)
|
||||
✔ the server log names no config path when the system config refuses (264.645827ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 39
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55236.597902
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 01224d15..0060886d 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -199,7 +199,6 @@ export function consoleReads({ root, system = null, configDir = null, rolesDir,
|
||||
let id = null, notifier;
|
||||
try {
|
||||
id = businessId({ business, dataRoot });
|
||||
- if (!dataRoot) throw new ReadError('not-configured', 'the Console has no system config, so it knows no notifier config');
|
||||
notifier = { binding: readNotifyConfig(dataRoot, id) };
|
||||
} catch (err) {
|
||||
notifier = { refused: err instanceof ReadError ? err.code : 'not-configured', message: err.message };
|
||||
@@ -0,0 +1,73 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4822.930259ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4906.180041ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5297.173253ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (223.878341ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (107.391341ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (75.159927ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2358.682472ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (255.202773ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (544.742006ms)
|
||||
✔ serve refuses a --business value that is not a business id (173.39112ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4876.075008ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1803.654173ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1821.439895ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1149.941427ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4803.794434ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53903.309626ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (6.491398ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.265801ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1355.269881ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (777.26881ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3261.915148ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (12.524282ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (7.871492ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.637237ms)
|
||||
✖ settings: release, board, loopback address, notifier binding name only (340.547186ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4934.323484ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23090.237373ms)
|
||||
✔ loopback host and board origin fail closed (9.603177ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (123.455381ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (158.397118ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1037.385946ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.43279ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.596386ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.831365ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8012.78546ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2567.614231ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5785.255819ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1932.619269ms)
|
||||
✔ the server log names no config path when the system config refuses (211.972681ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54368.703794
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:171:1
|
||||
✖ settings: release, board, loopback address, notifier binding name only (340.547186ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
+ message: 'The "path" argument must be of type string. Received null',
|
||||
- message: 'the Console has no system config, so it knows no notifier config',
|
||||
refused: 'not-configured'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/reads.test.mjs:197:10)
|
||||
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: { refused: 'not-configured', message: 'The "path" argument must be of type string. Received null' },
|
||||
expected: { refused: 'not-configured', message: 'the Console has no system config, so it knows no notifier config' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/queue-read.mjs b/packages/webui/src/queue-read.mjs
|
||||
index ae7e4509..33872c00 100644
|
||||
--- a/packages/webui/src/queue-read.mjs
|
||||
+++ b/packages/webui/src/queue-read.mjs
|
||||
@@ -15,7 +15,7 @@ try {
|
||||
const r = rows({});
|
||||
process.stdout.write(`${JSON.stringify({ rows: r.rows, notes: r.err })}\n`);
|
||||
} catch (err) {
|
||||
- const refused = QueueError !== null && err instanceof QueueError;
|
||||
+ const refused = err instanceof QueueError;
|
||||
process.stderr.write(`${refused ? err.message : 'the queue read failed'}\n`);
|
||||
process.exitCode = refused && Number.isInteger(err.code) && err.code > 0 ? err.code : 1;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3888.787759ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3951.652781ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5720.761076ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (291.120519ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (95.355614ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (58.784858ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2113.471315ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (204.506365ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.882927ms)
|
||||
✔ serve refuses a --business value that is not a business id (194.190736ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3869.638938ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3065.320252ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1639.493943ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1074.445443ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3722.111983ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54023.590411ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.775438ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.274238ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1277.909783ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1059.090152ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2350.726333ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.882718ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (57.294982ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.802986ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (880.371502ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3653.180772ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22968.27856ms)
|
||||
✔ loopback host and board origin fail closed (8.921972ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (129.379872ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (120.66428ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (990.201382ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.094147ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.145454ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.782519ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8085.779717ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2519.880683ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5631.08048ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2442.264334ms)
|
||||
✔ the server log names no config path when the system config refuses (268.674699ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 39
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54428.872021
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index affb9b7b..5be64e1b 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -349,7 +349,7 @@
|
||||
const afterRef = a => a && typeof a === 'object' ? `${qLink(a.id)}${a.when ? ` ${txt(a.when)}` : ''}` : qLink(a);
|
||||
const names = list => Array.isArray(list) && list.length ? list.map(txt).join(', ') : none;
|
||||
// The business file's arbiters are {delivery, technical}, each a role instance: "pm (delivery)".
|
||||
- const arbiterRefs = a => a && typeof a === 'object' && !Array.isArray(a) ? Object.entries(a).map(([kind, who]) => `${txt(who)} (${txt(kind)})`).join(', ') || none : names(a);
|
||||
+ const arbiterRefs = a => a && typeof a === 'object' && !Array.isArray(a) ? Object.entries(a).map(([kind, who]) => `${txt(who)} (${txt(kind)})`).join(', ') : names(a);
|
||||
const issueRefs = list => Array.isArray(list) && list.length ? list.map(n => `#${txt(n)}`).join(', ') : none;
|
||||
// Console serves no repository files, so the brief is its path and anchor, as the row names it.
|
||||
const briefRef = b => b?.path ? `<code>${txt(b.path)}${b.anchor ? `#${txt(b.anchor)}` : ''}</code>` : none;
|
||||
@@ -0,0 +1,48 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4322.024674ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4666.375347ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6140.987084ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (386.903021ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (492.311386ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (196.246538ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2460.30512ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (271.777808ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.687229ms)
|
||||
✔ serve refuses a --business value that is not a business id (219.591729ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4230.473238ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2158.338311ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1498.488241ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1012.482769ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4913.673987ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54314.8181ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.230514ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.284803ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2348.605136ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1248.777503ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (2560.261871ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.787973ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (12.51772ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (10.980412ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (229.462411ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4934.616438ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22092.124864ms)
|
||||
✔ loopback host and board origin fail closed (8.699044ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (122.191795ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (93.141873ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1884.047098ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.949058ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.951111ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.630552ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7340.548052ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2563.706796ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5795.365193ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2010.024848ms)
|
||||
✔ the server log names no config path when the system config refuses (213.678201ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 39
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54746.741731
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index affb9b7b..b3ffca18 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -346,7 +346,7 @@
|
||||
const qState = s => badge(s, QSTATE[s] || 'muted');
|
||||
const qLink = id => Number.isInteger(id) ? `<a href="#/queue/${esc(id)}">${esc(id)}</a>` : txt(id);
|
||||
// An after entry is {id, when}, as QUEUE.md writes it: "53 done".
|
||||
- const afterRef = a => a && typeof a === 'object' ? `${qLink(a.id)}${a.when ? ` ${txt(a.when)}` : ''}` : qLink(a);
|
||||
+ const afterRef = a => a && typeof a === 'object' ? `${qLink(a.id)}` : qLink(a);
|
||||
const names = list => Array.isArray(list) && list.length ? list.map(txt).join(', ') : none;
|
||||
// The business file's arbiters are {delivery, technical}, each a role instance: "pm (delivery)".
|
||||
const arbiterRefs = a => a && typeof a === 'object' && !Array.isArray(a) ? Object.entries(a).map(([kind, who]) => `${txt(who)} (${txt(kind)})`).join(', ') || none : names(a);
|
||||
@@ -0,0 +1,71 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4523.703633ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4893.533021ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5350.033243ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (211.332362ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (109.693508ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (58.412189ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2355.503452ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (337.597753ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.025656ms)
|
||||
✔ serve refuses a --business value that is not a business id (240.95955ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4515.146794ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2182.194444ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1533.719937ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1266.534823ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4503.163848ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53927.193787ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.126954ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.182791ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1376.292615ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1008.274679ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (3284.956445ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.314001ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (6.446331ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.121053ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (365.664886ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4499.700499ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22665.27668ms)
|
||||
✔ loopback host and board origin fail closed (8.266721ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (113.658913ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (99.996806ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1337.376123ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.262345ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.414247ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.899269ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8002.040069ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2577.38598ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5376.477477ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1313.394907ms)
|
||||
✔ the server log names no config path when the system config refuses (264.116342ms)
|
||||
ℹ tests 39
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54351.573732
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:205:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1313.394907ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ '<a href="#/queue/6">6</a>'
|
||||
- '<a href="#/queue/6">6</a> settled'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54b/mutwt/packages/webui/tests/views.test.mjs:219: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: '<a href="#/queue/6">6</a>',
|
||||
expected: '<a href="#/queue/6">6</a> settled',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
packages/queue/README.md: OK
|
||||
packages/queue/src/store.mjs: OK
|
||||
packages/queue/tests/write.test.mjs: OK
|
||||
packages/webui/README.md: OK
|
||||
packages/webui/src/cli.mjs: OK
|
||||
packages/webui/src/public/app.js: OK
|
||||
packages/webui/src/public/bus.js: OK
|
||||
packages/webui/src/queue-read.mjs: OK
|
||||
packages/webui/src/reads.mjs: OK
|
||||
packages/webui/src/serve.mjs: OK
|
||||
packages/webui/tests/reads-fixture.mjs: OK
|
||||
packages/webui/tests/reads.test.mjs: OK
|
||||
packages/webui/tests/shell.test.mjs: OK
|
||||
packages/webui/tests/views.test.mjs: OK
|
||||
@@ -0,0 +1,70 @@
|
||||
# Row 54 round 2 mutants: D01 to D31 from round 1 (D07 on the new redactor), E01 on for round 2's changes: exact text, must match once. Run from the worktree root.
|
||||
import sys
|
||||
A = "packages/webui/src/public/app.js"
|
||||
B = "packages/webui/src/public/bus.js"
|
||||
R = "packages/webui/src/reads.mjs"
|
||||
V = "packages/webui/src/serve.mjs"
|
||||
C = "packages/webui/src/cli.mjs"
|
||||
Q = "packages/queue/src/store.mjs"
|
||||
QR = "packages/webui/src/queue-read.mjs"
|
||||
M = {
|
||||
# my four items
|
||||
"D01": [(B, " clearTimeout(timer);\n const route = routeOf()", " const route = routeOf()")],
|
||||
"D02": [(B, ", h1: view.contains(a) && a.tagName === 'H1'", "")],
|
||||
"D03": [(B, " else if (k.h1) $('bus-view').querySelector('h1')?.focus({ preventScroll: true });\n", "")],
|
||||
"D04": [(A, " $('conv-pick').replaceChildren();", "")],
|
||||
"D05": [(A, " if (focused && document.activeElement === document.body) $('main').focus();\n", "")],
|
||||
"D06": [(A, "if (focused && document.activeElement === document.body)", "if (document.activeElement === document.body)")],
|
||||
# redaction and projection
|
||||
"D07": [(R, r"""return s.replace(/(^|[\s('"`=,[{<]|:(?!\/\/[^/]))~?\/[^\s'"`),;\]}>]+/g, '$1<path>').replace(""", "return s.replace(")],
|
||||
"D08": [(R, r""".replace(/(?<![\w.-])\d{17,20}(?![\w.-])/g, '<id>')""", "")],
|
||||
"D09": [(R, "let s = unquote(String(text));", "let s = String(text);")],
|
||||
"D10": [(R, "for (const [re, label] of known) s = s.replace(re, label);", "")],
|
||||
"D11": [(R, "return scrub({ rows: doc.rows.map(queueSummary), notes: doc.notes, at: new Date(now()).toISOString() }, redact);", "return { rows: doc.rows.map(queueSummary), notes: doc.notes, at: new Date(now()).toISOString() };")],
|
||||
"D12": [(R, "return scrub({ row, notes: doc.notes, at: new Date(now()).toISOString() }, redact);", "return { row, notes: doc.notes, at: new Date(now()).toISOString() };")],
|
||||
"D13": [(R, "vars: pick(b.vars, VAR_KEYS)", "vars: b.vars")],
|
||||
"D14": [(R, "vars: pick(r.vars, AGENT_KEYS)", "vars: r.vars")],
|
||||
"D15": [(R, "return { service, account, role, dateKind, date, state: credentialState(service, date, now) };", "return { ...ref, service, account, role, dateKind, date, state: credentialState(service, date, now) };")],
|
||||
"D16": [(R, "const fail = (code, message) => new ReadError(code, redact(message));", "const fail = (code, message) => new ReadError(code, message);")],
|
||||
"D17": [(R, "return scrub({ release, business: id, notifier, at: new Date(now()).toISOString() }, redact);", "return { release, business: id, notifier, at: new Date(now()).toISOString() };")],
|
||||
"D18": [(C, "(${redactor({ configDir: configDir() })(err.message)})", "(${err.message})")],
|
||||
# queue child
|
||||
"D19": [(R, "if (status === 2) return", "if (status === 3) return")],
|
||||
"D20": [(R, "if (size > maxBytes) {", "if (false) {")],
|
||||
"D21": [(R, "}, timeoutMs);", "}, timeoutMs * 1000);")],
|
||||
"D22": [(R, "if (!state?.live) throw", "if (!state) throw")],
|
||||
# routes
|
||||
"D23": [(V, " if (req.method !== 'GET') return json(res, 405, { error: 'method not allowed' });\n const a = server.address()", " const a = server.address()")],
|
||||
"D24": [(V, r"const QUEUE_ID = /^[1-9]\d{0,5}$/;", r"const QUEUE_ID = /^\d+$/;")],
|
||||
"D25": [(V, " if (!reads) return json(res, 503, { error: 'not-configured', message: 'the Console has no reads configured' });\n", "")],
|
||||
# views
|
||||
"D26": [(B, "['not-configured', 'no-bus-host', 'queue-refused'].includes(err.code)", "['not-configured', 'no-bus-host'].includes(err.code)")],
|
||||
"D27": [(B, "html = failedView(err) + (path === '/settings' ? appearance() : '');", "html = failedView(err);")],
|
||||
"D28": [(B, "...decisions, ...tasks, ...rows];", "...decisions, ...tasks];")],
|
||||
"D29": [(B, ", mirror: view.contains(a) ? a.dataset?.mirror : null", "")],
|
||||
"D30": [(B, "if (m) { const src = $(m.dataset.mirror); src.value = m.value; src.dispatchEvent(new Event('change')); return; }", "if (m) return;")],
|
||||
# store
|
||||
"D31": [(Q, "return { rows: rowsArray(r.state), err: r.notes, code: 0 };", "return { rows: rowsArray(r.state), err: [], code: 0 };")],
|
||||
# round 2 changes
|
||||
"E01": [(QR, "let QueueError = null;\n", "import { rows } from '../../queue/src/store.mjs';\nimport { QueueError as QE } from '../../queue/src/errors.mjs';\nlet QueueError = QE;\n"),
|
||||
(QR, " ({ QueueError } = await import('../../queue/src/errors.mjs'));\n const { rows } = await import('../../queue/src/store.mjs');\n", "")],
|
||||
"E02": [(R, "new ReadError('read-failed', `the queue read failed (exit ${status})`)", "new ReadError('read-failed', said || `the queue read failed (exit ${status})`)")],
|
||||
"E03": [(R, r"""|:(?!\/\/[^/]))~?\/""", r""")~?\/""")],
|
||||
"E04": [(R, r""":(?!\/\/[^/])""", ":")],
|
||||
"E05": [(R, r"""~?\/[^\s'""", r"""\/[^\s'""")],
|
||||
"E06": [(R, r"""[\s('"`=,[{<]|""", r"""[\s('"`=,[{]|""")],
|
||||
"E07": [(B, "<dd>${arbiterRefs(b.arbiters)}</dd>", "<dd>${names(b.arbiters)}</dd>")],
|
||||
"E08": [(B, "r.after.map(afterRef)", "r.after.map(qLink)")],
|
||||
"E09": [(B, "<dd>yes${Number.isFinite", "<dd>${txt(r.required)}${Number.isFinite")],
|
||||
"E10": [(B, "Number.isFinite(Date.parse(r.requiredSince))", "r.requiredSince")],
|
||||
"E11": [(R, " if (!dataRoot) throw new ReadError('not-configured', 'the Console has no system config, so it knows no notifier config');\n", "")],
|
||||
"E12": [(QR, "const refused = QueueError !== null && err instanceof QueueError;", "const refused = err instanceof QueueError;")],
|
||||
"E13": [(B, ".join(', ') || none : names(a);", ".join(', ') : names(a);")],
|
||||
"E14": [(B, "${a.when ? ` ${txt(a.when)}` : ''}", "")],
|
||||
}
|
||||
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")
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
# Each mutant against its package suite in a candidate worktree, then restores from the index.
|
||||
export TMPDIR=~/darkwing-scratch/r54b/tmp
|
||||
S=~/darkwing-scratch/r54b; M=$S/r2/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"\"([DE]\d\d)\"", open("'$M'/mutate.py").read())))))'); do
|
||||
(cd $W && git checkout -q -- packages/webui packages/queue && python3 $M/mutate.py $v > /dev/null && git diff > $M/$v.diff) || { echo "$v APPLY-FAILED" >> $M/summary.txt; continue; }
|
||||
pkg=webui; [ $v = D31 ] && pkg=queue
|
||||
(cd $W/packages/$pkg && 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 packages/queue && 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,8 @@
|
||||
#!/bin/bash
|
||||
# Prints each mutant's result and the test lines its failures point at.
|
||||
M=~/darkwing-scratch/r54b/r2/mut
|
||||
grep -E '^[DE][0-9][0-9] ' $M/summary.txt | while read -r id res rest; do
|
||||
sites=$(sed -n '/failing tests:/,$p' $M/$id.txt | grep -oE 'tests/[a-z0-9-]+\.test\.mjs:[0-9]+' | grep -v ':1$' | sort -u | tr '\n' ' ')
|
||||
names=$(sed -n '/failing tests:/,$p' $M/$id.txt | grep -E '^✖ ' | grep -v 'failing tests' | sed -E 's/^✖ //; s/ \([0-9.]+ms\)$//' | sort -u | tr '\n' '|')
|
||||
echo "$id $res | $sites| $names"
|
||||
done
|
||||
@@ -0,0 +1,48 @@
|
||||
start 2026-10-10T21:22:02Z
|
||||
D01 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D02 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D03 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D04 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D05 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D06 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D07 killed exit=1 ℹ pass 35 ℹ fail 4
|
||||
D08 killed exit=1 ℹ pass 36 ℹ fail 3
|
||||
D09 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D10 killed exit=1 ℹ pass 35 ℹ fail 4
|
||||
D11 killed exit=1 ℹ pass 37 ℹ fail 2
|
||||
D12 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D13 killed exit=1 ℹ pass 37 ℹ fail 2
|
||||
D14 SURVIVED exit=0 ℹ pass 39 ℹ fail 0
|
||||
D15 killed exit=1 ℹ pass 37 ℹ fail 2
|
||||
D16 killed exit=1 ℹ pass 37 ℹ fail 2
|
||||
D17 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D18 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D19 killed exit=1 ℹ pass 37 ℹ fail 2
|
||||
D20 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D21 killed exit=1 ℹ pass 38 ℹ fail 0
|
||||
D22 SURVIVED exit=0 ℹ pass 39 ℹ fail 0
|
||||
D23 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D24 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D25 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D26 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D27 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D28 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D29 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D30 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
D31 killed exit=1 ℹ pass 148 ℹ fail 1
|
||||
E01 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
E02 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
E03 killed exit=1 ℹ pass 36 ℹ fail 3
|
||||
E04 killed exit=1 ℹ pass 37 ℹ fail 2
|
||||
E05 killed exit=1 ℹ pass 36 ℹ fail 3
|
||||
E06 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
E07 killed exit=1 ℹ pass 37 ℹ fail 2
|
||||
E08 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
E09 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
E10 SURVIVED exit=0 ℹ pass 39 ℹ fail 0
|
||||
E11 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
E12 SURVIVED exit=0 ℹ pass 39 ℹ fail 0
|
||||
E13 SURVIVED exit=0 ℹ pass 39 ℹ fail 0
|
||||
E14 killed exit=1 ℹ pass 38 ℹ fail 1
|
||||
end 2026-10-10T22:07:37Z
|
||||
DONE
|
||||
@@ -0,0 +1 @@
|
||||
tokens.css is current
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (1.358293ms)
|
||||
✔ the fixture business validates and comes back frozen (4.319109ms)
|
||||
✔ two instances may share a definition (1.396227ms)
|
||||
✔ top-level refusals (4.055262ms)
|
||||
✔ arbiters and projects (7.498012ms)
|
||||
✔ role instances (3.986412ms)
|
||||
✔ Vikunja bots (8.432332ms)
|
||||
✔ a role without Vikunja takes no tracker block (3.583648ms)
|
||||
✔ credential references match the definition's services (3.569963ms)
|
||||
✔ launch (9.358031ms)
|
||||
✔ loadBusiness: file checks (1.91489ms)
|
||||
✔ loadBusiness: not a regular file (41.934608ms)
|
||||
✔ loading writes nothing (1.116741ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.823344ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.554849ms)
|
||||
✔ usage errors exit 4 (280.926956ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (60.874599ms)
|
||||
✔ validate: project files (309.472006ms)
|
||||
✔ validate: missing files and a broken system config (240.996764ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (63.782088ms)
|
||||
✔ validate: a token file inside the repository is refused (64.100441ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (199.841638ms)
|
||||
✔ resolve: prints one instance's record (195.243118ms)
|
||||
✔ resolve: refusals (391.947059ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (2.48624ms)
|
||||
✔ check: a good file has no problems (0.818496ms)
|
||||
✔ check never opens the file: a write-only token passes (0.334117ms)
|
||||
✔ check: file problems (0.873893ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.812928ms)
|
||||
✔ check: dates and environment references (0.525454ms)
|
||||
✔ path and load (2.561329ms)
|
||||
✔ refusals (1.291396ms)
|
||||
✔ systemVars flattens the validated config (1.952187ms)
|
||||
✔ precedence: system, business, project, project role, agent (6.651473ms)
|
||||
✔ limits narrow the definition and never widen it (2.836445ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (5.06657ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (2.441279ms)
|
||||
✔ limits.authority narrows cross-role actions too (1.426583ms)
|
||||
✔ classify (1.598098ms)
|
||||
✔ the record carries what the broker and launcher need (1.419734ms)
|
||||
✔ digest: key order doesn't matter, any value change does (18.247732ms)
|
||||
✔ refusals (2.333964ms)
|
||||
✔ the four shipped version 2 roles load (3.985346ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.378316ms)
|
||||
✔ shipped authority follows the note's table (0.770182ms)
|
||||
✔ version 1 files keep loading with no authority (1.288688ms)
|
||||
✔ the conductor policy isn't a role (0.298652ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.519974ms)
|
||||
✔ version 2 refusals (2.025378ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (2.901348ms)
|
||||
✔ credentials: Gitea scopes (1.426595ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.586912ms)
|
||||
✔ credentials: services (0.786069ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (0.765377ms)
|
||||
✔ every key names known layers and a merge rule (1.091141ms)
|
||||
✔ unknown keys and wrong layers refuse (0.703586ms)
|
||||
✔ types (1.723076ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.270166ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.323513ms)
|
||||
✔ merge doesn't change its inputs (0.12932ms)
|
||||
ℹ tests 60
|
||||
ℹ suites 0
|
||||
ℹ pass 60
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1880.166622
|
||||
@@ -0,0 +1,159 @@
|
||||
✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1706.172781ms)
|
||||
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1286.359657ms)
|
||||
ℹ git commit -e: index.lock free during the editor
|
||||
✔ F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update (1241.840481ms)
|
||||
ℹ git commit -e -- src.txt: index.lock held during the editor
|
||||
✔ F1: step 8 with index.lock held exits 3, and ordinary commits stay refused until the printed command runs (1297.494888ms)
|
||||
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1245.085287ms)
|
||||
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1247.854944ms)
|
||||
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1695.611309ms)
|
||||
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1292.930871ms)
|
||||
✔ F1: a shared-index change during the procedure is not committed (1256.919569ms)
|
||||
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1255.561012ms)
|
||||
✔ F1: a missing or a different hook refuses (906.609279ms)
|
||||
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (4859.566789ms)
|
||||
✔ F1: the canary refuses a hook that git would not run (1511.922455ms)
|
||||
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1565.127039ms)
|
||||
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (1373.944257ms)
|
||||
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (1303.326184ms)
|
||||
✔ bootstrap: the archived tests and validator run outside any repository (1095.80342ms)
|
||||
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (986.911079ms)
|
||||
✔ general: a queue write after the snapshot is not committed (1456.064641ms)
|
||||
✔ general: a snapshot whose log does not extend the base refuses (1018.585156ms)
|
||||
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (157.799269ms)
|
||||
✔ general: environment overrides, a linked worktree and usage (681.538055ms)
|
||||
✔ general: a queue path staged before the run refuses at step 1 (700.427566ms)
|
||||
✔ general: HEAD's queue tests failing in the archive refuse (1091.531507ms)
|
||||
✔ genesis document serializes deterministically and replays (4.714347ms)
|
||||
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (3.134875ms)
|
||||
✔ a tampered result, receipt or viewSha fails replay (3.554262ms)
|
||||
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.21945ms)
|
||||
✔ add: defaults for an ordinary seat, privileged extras, refusals (5.927961ms)
|
||||
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (8.269384ms)
|
||||
✔ matrix: release, review round, changes requested and waiting-on-jason (16.848162ms)
|
||||
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (12.104397ms)
|
||||
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (31.412102ms)
|
||||
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (3.765794ms)
|
||||
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1951.538792ms)
|
||||
✔ matrix: blocked keeps the claim and returns only to previousState (4.328121ms)
|
||||
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.794808ms)
|
||||
✔ field edits: who may change what (5.753757ms)
|
||||
✔ set issues keeps a logged narrowing of closes (N10) (3.220109ms)
|
||||
✔ text the table shows refuses \ and <, everywhere it enters (N8) (2.09099ms)
|
||||
✔ every accepted text renders to nine cells on every row (N8) (30.500822ms)
|
||||
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.604006ms)
|
||||
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.779968ms)
|
||||
✔ replay holds every op id to the caller's rule (N11) (7.113606ms)
|
||||
✔ note: owner, listed reviewer or privileged; empty clears (1.540375ms)
|
||||
✔ assign moves the claim with the owner; done clears it (2.812568ms)
|
||||
✔ render is byte-stable and escapes pipes (0.538688ms)
|
||||
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.692321ms)
|
||||
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (17.828541ms)
|
||||
✔ canonical args make a retry's identity independent of list order (0.308093ms)
|
||||
✔ manifests, headings and blob ids (0.412858ms)
|
||||
✔ the migration map: one queue-map block, exact keys (0.552853ms)
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (804.147865ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (295.722132ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.470191ms)
|
||||
✔ acquire publishes the record by link; release removes only its own lock (14.890177ms)
|
||||
✔ a kill between the temp write and the link leaves no lock (65.73674ms)
|
||||
✔ a short or failed temp write refuses and leaves no lock and no temp (10.501895ms)
|
||||
✔ a link error other than EEXIST refuses (10.166399ms)
|
||||
✔ an error after the link releases the lock: unreadable gate, failing temp stat (22.229286ms)
|
||||
✔ a release that fails on a gate path is reported, never a stack trace (P1) (28.664565ms)
|
||||
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10139.893336ms)
|
||||
✔ two concurrent unlockers: the second refuses on the gate (54.781002ms)
|
||||
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (70.240457ms)
|
||||
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (87.401918ms)
|
||||
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (82.533562ms)
|
||||
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (26.135671ms)
|
||||
✔ the same pid and start on a different boot is mismatch (0.461054ms)
|
||||
✔ a foreign host is unknown whatever the local pid says; unlock refuses (110.488811ms)
|
||||
✔ unreadable /proc: classification is unknown and acquire refuses (0.749191ms)
|
||||
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.208151ms)
|
||||
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (33.269405ms)
|
||||
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (32.334008ms)
|
||||
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (15.667112ms)
|
||||
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (52.241849ms)
|
||||
✔ the migration map validates and renders the golden genesis table (4.663539ms)
|
||||
✔ the marked QUEUE.md holds every row and parked item between its markers (1.591298ms)
|
||||
✔ map-check reports each kind of drift (5.444821ms)
|
||||
✔ a request posts once as the requester; a retry sends nothing (1064.346952ms)
|
||||
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (5261.987012ms)
|
||||
✔ the pre-send checks: GET user must name the requester, under the deadline (1842.87173ms)
|
||||
✔ the lead's request refuses a token for login sage (1200.862475ms)
|
||||
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (1442.138917ms)
|
||||
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2940.29315ms)
|
||||
✔ a same-op retry after a kill sends nothing, even with a stale view (7119.351208ms)
|
||||
✔ a held lock at the outcome exits 3 and names what the transport said (898.409724ms)
|
||||
✔ late outcomes: after an abandon, and after a resolve with the same or another id (3340.371403ms)
|
||||
✔ resolve checks the comment: issue, markers, round, candidate and author (1940.283066ms)
|
||||
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (828.082544ms)
|
||||
✔ validateRow checks a request round's shape, which every replayed entry must keep (528.324032ms)
|
||||
✔ request, changes, a new candidate, approval: every round pinned; no review files (1807.647723ms)
|
||||
✔ a row with no reviewers opens a round that posts nothing (1379.509751ms)
|
||||
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1567.982266ms)
|
||||
✔ semantics: v1 entries replay as before; review entries need v2 (592.904042ms)
|
||||
✔ set reviewers refuses the row's owner (2026-09-28) (380.040787ms)
|
||||
✔ the owner records no verdict, even as a listed reviewer (635.322725ms)
|
||||
✔ a request comment over the length limit is not sent (737.345262ms)
|
||||
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (688.463801ms)
|
||||
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (3749.621101ms)
|
||||
✔ genesis: refusals before anything is written (587.74823ms)
|
||||
✔ genesis: the map must be committed, well formed, with committed briefs and seats (826.0597ms)
|
||||
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (713.940647ms)
|
||||
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (473.417922ms)
|
||||
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (784.678148ms)
|
||||
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (1071.98683ms)
|
||||
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (888.989873ms)
|
||||
✔ a retried add returns the id it first allocated, after reassignment and after done (1085.59566ms)
|
||||
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (844.657744ms)
|
||||
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1497.029569ms)
|
||||
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (1054.298666ms)
|
||||
✔ add, set reviewers and assign refuse the row's owner as a reviewer (777.115222ms)
|
||||
✔ the working-brief check: a changed working copy refuses the start and flags next (875.330423ms)
|
||||
✔ next: resume first, then nothing for an idle seat; needs a seat (391.124341ms)
|
||||
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (898.135557ms)
|
||||
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1324.996601ms)
|
||||
✔ a hand edit to queue.json refuses every verb, reads included (831.879171ms)
|
||||
✔ verify and render --check leave bytes and mtimes unchanged (1160.179129ms)
|
||||
✔ render is byte-stable across runs and repositories (1305.820508ms)
|
||||
✔ snapshot and verify --snapshot (2480.182337ms)
|
||||
✔ usage errors exit 4 (1021.919086ms)
|
||||
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (357.402798ms)
|
||||
✔ a rename failure: nothing visible, temp removed (243.387888ms)
|
||||
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (322.220165ms)
|
||||
✔ a directory fsync failure, then sync names the op (503.320478ms)
|
||||
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (273.888381ms)
|
||||
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (219.385393ms)
|
||||
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (259.005141ms)
|
||||
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (211.529693ms)
|
||||
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (264.742249ms)
|
||||
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (242.657359ms)
|
||||
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (195.864438ms)
|
||||
✔ a view write that fails keeps the op and reports a stale view (262.82204ms)
|
||||
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (758.014233ms)
|
||||
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (787.537175ms)
|
||||
✔ SIGKILL after the witness, before the view: the stale refusal names the op (708.651878ms)
|
||||
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (729.177763ms)
|
||||
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (278.079412ms)
|
||||
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1272.927575ms)
|
||||
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (372.724295ms)
|
||||
✔ a header edit during a write: the op stands, the view write is skipped with a warning (288.878156ms)
|
||||
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (294.123846ms)
|
||||
✔ rows: every row as show prints it, in list order; writes nothing; same notes and refusals as list (329.091403ms)
|
||||
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (313.610048ms)
|
||||
✔ a writer paused before and after the witness rename: readers see a tail, then a match (330.849298ms)
|
||||
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (692.622594ms)
|
||||
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (722.805732ms)
|
||||
✔ the platform check refuses other filesystems (208.31301ms)
|
||||
✔ tmpfs passes only a test layer that allows it (N5) (314.598247ms)
|
||||
✔ unlock keeps a multi-line lock record on stdout (P3) (235.596901ms)
|
||||
ℹ tests 149
|
||||
ℹ suites 0
|
||||
ℹ pass 149
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 40044.001884
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user