review(row54): round 1 packet, changes (darkwing)
Review of #1543 candidate dba2429e: gate green with Docker, 31 mutants (26 killed), Node, import and Chromium probes. Posted as comment 27186. 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
|
||||
cd146196c98d8492a2b7f0c9bc046db0a69bd8c4539de8454563bd91cda24d9b packages/webui/README.md
|
||||
07d2c06523dd89f82c9ea14757852b16434f7207df57d7dfcbc58425e6e75c1d packages/webui/src/cli.mjs
|
||||
5a611e2339fa9e31c489d24460f82852ca2bc892f07265ffdc5eca03b579541b packages/webui/src/public/app.js
|
||||
0e1305c1cfb27617c8df66c77a5bf0fca822d65b9da8f75aadc27e11fdf7c176 packages/webui/src/public/bus.js
|
||||
4b10c9522f7d2061853abbc98b1d8425791ebde0f9a3af2ef7f19119c23c5cb6 packages/webui/src/queue-read.mjs
|
||||
1e7505a9cb2509d97dd671399aca8bb5f68ea72320c8f6cb0648c8f0eed9db31 packages/webui/src/reads.mjs
|
||||
8121915343ad5000cf66d474d9e4a994ce6c44ff1977822a067ad5ffd2fbbf29 packages/webui/src/serve.mjs
|
||||
f1896e52c29ab4d211a946a70c0d94cecedfb4a0099f8dc37b42e10f55377bc8 packages/webui/tests/reads-fixture.mjs
|
||||
9eaad7bdf3159e68715b2f63ba3a7d03746ea87ff9ae42cb36b2205639b39f44 packages/webui/tests/reads.test.mjs
|
||||
85f3bd247e6f99da717a51d71db13b65d511fffa8f0232ecbafa6c5000430c4e packages/webui/tests/shell.test.mjs
|
||||
c3b23f81dfc5a88e1a0865dbabfc8168ba4a0749cc171bb21615e2c113b46e57 packages/webui/tests/views.test.mjs
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (7419.429981ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7680.637771ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (13386.420409ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (1345.265196ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (218.103567ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (97.857933ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1828.410005ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (1012.495009ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (531.174294ms)
|
||||
✔ serve refuses a --business value that is not a business id (186.842156ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (7047.334736ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (6246.922456ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (4389.254684ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (828.95027ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (7468.513781ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (57162.25451ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.999965ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.448513ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2070.482221ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (3676.131063ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.417875ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (36.892944ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (12.873755ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1249.123547ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (7322.498686ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24203.485317ms)
|
||||
✔ loopback host and board origin fail closed (9.007024ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (160.476511ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (192.38851ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1444.12178ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (18.747124ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (27.71902ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.299374ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (10524.470842ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (6111.154323ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (7650.945522ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (6902.541038ms)
|
||||
✔ the server log names no config path when the system config refuses (212.908855ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 57595.214628
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5839.746242ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (7325.85568ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7831.155187ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (347.66314ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (173.94975ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (55.724887ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2498.033109ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (207.637636ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.179874ms)
|
||||
✔ serve refuses a --business value that is not a business id (209.950815ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (6068.495613ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4283.951068ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (11919.124163ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (5634.883862ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5434.844012ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56209.30624ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.036858ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.290985ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1592.371831ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1299.104554ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (15.730037ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (126.356697ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.833237ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1264.596881ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (6807.764916ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (27296.723771ms)
|
||||
✔ loopback host and board origin fail closed (9.197514ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (133.750629ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (132.277584ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1378.640156ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.363383ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.500626ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.773941ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (10675.98392ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (11032.844508ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (7573.153409ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (10210.286256ms)
|
||||
✔ the server log names no config path when the system config refuses (231.763576ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56651.663371
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4286.355057ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4252.126397ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6854.737148ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (397.293903ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (892.224846ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (305.290964ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2499.061199ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (229.570056ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.950102ms)
|
||||
✔ serve refuses a --business value that is not a business id (202.430622ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4080.000342ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2902.704951ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1725.712723ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1039.767926ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4243.701844ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55322.312724ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.003187ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.266493ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2653.500347ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1455.707416ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (15.527602ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (16.109401ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (20.501815ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (918.620874ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4178.952002ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22508.319055ms)
|
||||
✔ loopback host and board origin fail closed (8.615004ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (223.229573ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (162.65381ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1995.402535ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.228437ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.745638ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.218172ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7533.989319ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3083.283406ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5067.081944ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2526.450229ms)
|
||||
✔ the server log names no config path when the system config refuses (319.428027ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55822.82058
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4724.290753ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4722.707574ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6690.234269ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (447.173015ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (132.577517ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (60.410292ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2308.918023ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (492.048171ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.459549ms)
|
||||
✔ serve refuses a --business value that is not a business id (298.626669ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4817.249057ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2453.444874ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1271.507ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (893.14175ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4719.168513ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55313.079262ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.890544ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.298797ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1374.358329ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (2442.351205ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (12.412173ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (27.071614ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (13.071699ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1005.23027ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5012.615125ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24057.460238ms)
|
||||
✔ loopback host and board origin fail closed (7.811045ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (117.698074ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (216.386573ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1189.511684ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.333266ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.447183ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.816484ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8807.266513ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2729.494701ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5756.971116ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1679.248145ms)
|
||||
✔ the server log names no config path when the system config refuses (265.944172ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55866.804231
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5394.913107ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5618.203116ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5584.792046ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (306.257569ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (83.303554ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (49.559503ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2194.013714ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (221.629238ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (688.121733ms)
|
||||
✔ serve refuses a --business value that is not a business id (173.917439ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5420.340201ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2013.951367ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1761.145701ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (985.9ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5585.269091ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53598.657341ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.745622ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.213752ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1200.796259ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1035.157402ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (27.175479ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (67.165167ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (24.32399ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (2165.271905ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5432.822289ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22935.878861ms)
|
||||
✔ loopback host and board origin fail closed (7.111826ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (113.133151ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (86.62178ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1032.056695ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.108678ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.079698ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.572088ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8084.586836ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3050.371572ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5506.865857ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2213.02114ms)
|
||||
✔ the server log names no config path when the system config refuses (323.726956ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54013.508053
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4967.867343ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5054.379982ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7767.995099ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (351.660239ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (229.866655ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (248.529647ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2443.280612ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (754.62238ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (555.937246ms)
|
||||
✔ serve refuses a --business value that is not a business id (203.046485ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4962.873181ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2255.494312ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1835.688725ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (874.471396ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5057.229424ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55917.71064ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.578516ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.208763ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1807.02286ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (2925.352176ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (10.321829ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (68.195812ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (7.821254ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (430.255959ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4966.190023ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23030.997892ms)
|
||||
✔ loopback host and board origin fail closed (8.231493ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (145.679402ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (192.789916ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1468.72478ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.927827ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.066366ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.585746ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7706.035404ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3229.754636ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5263.502407ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1949.682779ms)
|
||||
✔ the server log names no config path when the system config refuses (490.821529ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56682.506308
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5281.957391ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5241.65014ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7840.135378ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (270.540486ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (208.09862ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (318.694936ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2758.51859ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (147.192096ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (550.440101ms)
|
||||
✔ serve refuses a --business value that is not a business id (178.662668ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5118.818508ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3077.747047ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1627.474771ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1029.062199ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5290.335757ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (56468.112243ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.166496ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.372441ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2103.086677ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (2703.057076ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (7.65459ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (41.611595ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (23.322504ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (856.256666ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5115.288055ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23151.949075ms)
|
||||
✔ loopback host and board origin fail closed (9.284164ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (129.004745ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (89.098167ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2105.449159ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.570082ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.761389ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.906565ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7747.877498ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2783.880721ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5730.815205ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3355.333275ms)
|
||||
✔ the server log names no config path when the system config refuses (216.073819ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56933.604909
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (6327.766018ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6587.561462ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7164.072836ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (246.078084ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (139.195353ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (95.893839ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3105.093089ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (213.372392ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.299281ms)
|
||||
✔ serve refuses a --business value that is not a business id (232.303138ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4227.223124ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3532.965806ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2264.28452ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1354.462899ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5789.719539ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55222.570505ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.328296ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.284846ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2212.985953ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1389.946627ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (10.094467ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (60.349154ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (6.569104ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (2564.866182ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4188.479774ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23166.897511ms)
|
||||
✔ loopback host and board origin fail closed (8.473148ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (124.260907ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (90.028219ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1862.766827ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.430597ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.440465ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.909832ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7536.091806ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3049.744251ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (6628.913916ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2676.092518ms)
|
||||
✔ the server log names no config path when the system config refuses (212.437536ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55715.675637
|
||||
@@ -0,0 +1,8 @@
|
||||
full-1 exit=0 ℹ pass 38 ℹ fail 0
|
||||
full-2 exit=0 ℹ pass 38 ℹ fail 0
|
||||
full-3 exit=0 ℹ pass 38 ℹ fail 0
|
||||
full-4 exit=0 ℹ pass 38 ℹ fail 0
|
||||
full-5 exit=0 ℹ pass 38 ℹ fail 0
|
||||
full-6 exit=0 ℹ pass 38 ℹ fail 0
|
||||
full-7 exit=0 ℹ pass 38 ℹ fail 0
|
||||
full-8 exit=0 ℹ pass 38 ℹ fail 0
|
||||
+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/r54a/tmp DOCKER_HOST=unix:///nonexistent.sock
|
||||
cd ~/darkwing-scratch/r54a/papply
|
||||
O=~/darkwing-scratch/r54a/r1/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 ef13c793..0a20da6b 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -542,7 +542,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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3553.895836ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4891.433739ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6956.718189ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (389.653748ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (360.721408ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (259.738493ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2914.51749ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (218.470323ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.920229ms)
|
||||
✔ serve refuses a --business value that is not a business id (206.73927ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4420.305151ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2686.360689ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2011.768104ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1442.624355ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3680.914258ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54948.713021ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.936711ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.285537ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2664.55476ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1076.031935ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (45.708991ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (49.193628ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (10.462066ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1145.745393ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3665.782998ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22693.262252ms)
|
||||
✔ loopback host and board origin fail closed (8.121504ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (147.592865ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (296.909995ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2227.652587ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (5.246242ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (7.875649ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.008695ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7278.461772ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3423.807951ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4781.975041ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3724.530622ms)
|
||||
✔ the server log names no config path when the system config refuses (213.795611ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55507.970242
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4781.975041ms)
|
||||
AssertionError [ERR_ASSERTION]: the navigation cleared the due refresh
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:150:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async 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 ef13c793..5908e8bd 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -529,7 +529,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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3968.619637ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5047.49366ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6459.792237ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (314.357486ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (101.425172ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (119.06869ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2076.637029ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (220.20531ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.69311ms)
|
||||
✔ serve refuses a --business value that is not a business id (210.542338ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4061.401789ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2753.455344ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1393.137759ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1437.780504ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4679.185454ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54300.189462ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.701029ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.253638ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1325.561302ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1458.997085ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (14.260186ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (32.239843ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (32.032266ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (880.22299ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4525.040771ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22860.897185ms)
|
||||
✔ loopback host and board origin fail closed (9.145343ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (135.226621ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (112.161171ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (947.30215ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.167311ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.417363ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.745934ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7682.084505ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2736.481331ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4385.276082ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2773.495323ms)
|
||||
✔ the server log names no config path when the system config refuses (211.527649ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54686.891864
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (4385.276082ms)
|
||||
AssertionError [ERR_ASSERTION]: #/queue keeps heading focus
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:95: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 ef13c793..ed7d83b1 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -538,7 +538,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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4959.135819ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5474.161015ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5830.919645ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (245.718215ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (110.347818ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (55.071907ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2216.361773ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (261.823267ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (534.849259ms)
|
||||
✔ serve refuses a --business value that is not a business id (209.384483ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4258.679146ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2859.495657ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1210.012664ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1270.574713ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5212.633077ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54375.108638ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.008832ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.261643ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1408.723324ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (958.62181ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (50.388295ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (79.938741ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (26.881936ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1300.646496ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4240.6632ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24038.570056ms)
|
||||
✔ loopback host and board origin fail closed (11.500921ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (151.330163ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (147.370812ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1278.214507ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.441027ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.553354ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.098882ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8038.81786ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2561.130024ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5077.605025ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2121.25286ms)
|
||||
✔ the server log names no config path when the system config refuses (318.053875ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54807.670851
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5077.605025ms)
|
||||
AssertionError [ERR_ASSERTION]: #/queue keeps heading focus
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:95: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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5416.256047ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5630.022554ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7285.022751ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (251.074506ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (96.863255ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (198.437726ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2880.248817ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (162.972556ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (726.021417ms)
|
||||
✔ serve refuses a --business value that is not a business id (179.377266ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5291.097952ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3133.676466ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1361.246225ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (808.287257ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5531.156192ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54931.370367ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.988747ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.268064ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1865.443672ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1787.964736ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.768333ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (48.559844ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (13.790316ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1553.642343ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5277.71839ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24077.83055ms)
|
||||
✔ loopback host and board origin fail closed (8.965747ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (130.838291ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (94.321597ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1719.091845ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.239607ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.415696ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.843268ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5348.746927ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (4085.043051ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5597.170405ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2798.86353ms)
|
||||
✔ the server log names no config path when the system config refuses (319.853437ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55378.019619
|
||||
|
||||
✖ 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 (5348.746927ms)
|
||||
AssertionError [ERR_ASSERTION]: no stale session in the picker
|
||||
|
||||
1 !== 0
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4615.719375ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4585.514852ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7271.250051ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (242.896934ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (220.669235ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (141.578121ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2988.599285ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (126.433258ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.917971ms)
|
||||
✔ serve refuses a --business value that is not a business id (195.271174ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4479.853577ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3174.276657ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1640.838159ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (775.790906ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4595.451589ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55470.169655ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.614121ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.223081ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2109.365496ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (2184.175929ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (8.080197ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (25.283905ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (17.008779ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (335.452271ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4597.129667ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22498.542313ms)
|
||||
✔ loopback host and board origin fail closed (9.263759ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (136.050609ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (128.243855ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2210.233039ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.315777ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.867949ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.844346ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (4472.420123ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (4783.30299ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5506.470634ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2600.39773ms)
|
||||
✔ the server log names no config path when the system config refuses (260.593252ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56411.185578
|
||||
|
||||
✖ 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 (4472.420123ms)
|
||||
AssertionError [ERR_ASSERTION]: focus after a refusal closes the conversation
|
||||
|
||||
'' !== 'main'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/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,76 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3613.786889ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4240.276866ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5973.742507ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (466.99216ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (105.247532ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (130.073438ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3065.220086ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (230.729041ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (558.243876ms)
|
||||
✔ serve refuses a --business value that is not a business id (184.29222ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4047.318596ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2916.465219ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1192.341421ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (870.513825ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3063.102811ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54285.29228ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.060755ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.282176ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2246.692501ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (793.260067ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (14.965494ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (11.07886ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (14.771348ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1090.883299ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3131.077537ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22611.758067ms)
|
||||
✔ loopback host and board origin fail closed (8.416311ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (128.240361ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (152.805559ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1934.709995ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.240145ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.239792ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.470294ms)
|
||||
✖ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (3230.457287ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3242.981076ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (14732.61445ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1262.366048ms)
|
||||
✔ the server log names no config path when the system config refuses (211.323102ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54920.869466
|
||||
|
||||
✖ 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 (3230.457287ms)
|
||||
AssertionError [ERR_ASSERTION]: a failure with nothing focused leaves focus alone
|
||||
|
||||
false !== true
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/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'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (14732.61445ms)
|
||||
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/r54a/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/r54a/mutwt/packages/webui/tests/views.test.mjs:167:34)
|
||||
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/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 0443a7c9..3c6717c0 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -41,7 +41,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,120 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4650.582262ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4664.085582ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5754.318128ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (279.717506ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (114.210506ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (53.435062ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2221.997841ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (218.985409ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.312794ms)
|
||||
✔ serve refuses a --business value that is not a business id (179.869563ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4508.168686ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2293.264018ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1319.817439ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1205.060938ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4581.550733ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53751.394015ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.762145ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.322334ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (730.976411ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (783.263853ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (36.53287ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (55.40206ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (21.82087ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (729.334788ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4574.89748ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23349.130426ms)
|
||||
✔ loopback host and board origin fail closed (7.012201ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (122.031329ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (110.378886ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1061.933973ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.30578ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.451548ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.782154ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7613.991981ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2495.761305ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15029.35374ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1029.333274ms)
|
||||
✔ the server log names no config path when the system config refuses (212.197114ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 34
|
||||
ℹ fail 4
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54179.015502
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:19:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.762145ms)
|
||||
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/r54a/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: '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:40:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (730.976411ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at /srv/secret/x.token for <id>'
|
||||
- 'token at <path> for <id>'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:49: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>',
|
||||
expected: 'token at <path> for <id>',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15029.35374ms)
|
||||
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/r54a/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/r54a/mutwt/packages/webui/tests/views.test.mjs:167:34)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
|
||||
|
||||
test at tests/views.test.mjs:185:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1029.333274ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries /srv/secret
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads-fixture.mjs:54:185)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:191:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:192: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 0443a7c9..00fa848c 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -41,7 +41,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 (5688.449075ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5694.95127ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7107.443888ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (343.91401ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (89.658458ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (83.906383ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2290.543537ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (367.513325ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.358747ms)
|
||||
✔ serve refuses a --business value that is not a business id (186.786281ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5610.619897ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1942.713594ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1204.744965ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1072.867685ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5631.191537ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54777.514235ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (4.211688ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.340018ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (685.64992ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1303.125605ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (24.025165ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (77.22646ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (14.685298ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (2276.993614ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5653.991032ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24629.657301ms)
|
||||
✔ loopback host and board origin fail closed (8.698518ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (117.702268ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (99.809366ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1022.425725ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.31903ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.399609ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.81104ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8241.020683ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2564.16633ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5781.32492ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1711.303273ms)
|
||||
✔ the server log names no config path when the system config refuses (215.065833ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 35
|
||||
ℹ fail 3
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55203.672143
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:19:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (4.211688ms)
|
||||
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/r54a/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: 'user 123456789012345678 ok',
|
||||
expected: 'user <id> ok',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/reads.test.mjs:40:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (685.64992ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at <path> for 523456789012345678'
|
||||
- 'token at <path> for <id>'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:49: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',
|
||||
expected: 'token at <path> for <id>',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:185:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1711.303273ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries id 523456789012345678
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads-fixture.mjs:53:38)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:191:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:192: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 0443a7c9..2e411db5 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -39,7 +39,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,77 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5175.312212ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5665.118462ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5853.659579ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (250.913924ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (137.674136ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (110.570377ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2323.48959ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (236.080781ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (557.134857ms)
|
||||
✔ serve refuses a --business value that is not a business id (240.965551ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5286.630849ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1924.346256ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1138.222866ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1192.055963ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5513.038574ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54364.222627ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.151142ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.336056ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1454.573847ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (829.865022ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (65.075001ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (47.810417ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (66.425429ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1679.554601ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5084.063744ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24647.259793ms)
|
||||
✔ loopback host and board origin fail closed (9.974437ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (132.543531ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (95.015033ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1069.560663ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.103773ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.524ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.950677ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8179.983322ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2679.061779ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15617.419591ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1174.356514ms)
|
||||
✔ the server log names no config path when the system config refuses (211.037161ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54769.880455
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:19:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.151142ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
true !== false
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/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: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15617.419591ms)
|
||||
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/r54a/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/r54a/mutwt/packages/webui/tests/views.test.mjs:167:34)
|
||||
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/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 0443a7c9..f4c19dcb 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -40,7 +40,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,131 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4821.548654ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4712.402677ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5063.478583ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (333.96204ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (95.492452ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (146.016361ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2132.328901ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (179.840217ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.852334ms)
|
||||
✔ serve refuses a --business value that is not a business id (183.777864ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4691.507317ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1782.431855ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1083.690064ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (905.133089ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4774.780471ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54058.753561ms)
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.109248ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.312537ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1348.652243ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (911.277874ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (53.102001ms)
|
||||
✖ business: missing or invalid file refuses with the module's text, redacted (13.446815ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (58.631252ms)
|
||||
✖ settings: release, board, loopback address, notifier binding name only (1725.215019ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4710.06304ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24419.701727ms)
|
||||
✔ loopback host and board origin fail closed (7.568747ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (127.869708ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (109.641548ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1109.060295ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.037359ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.80034ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.352675ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7673.066897ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2596.719124ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4979.78513ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1436.423592ms)
|
||||
✔ the server log names no config path when the system config refuses (216.073464ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 34
|
||||
ℹ fail 4
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54464.117032
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:19:1
|
||||
✖ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.109248ms)
|
||||
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/r54a/mutwt/packages/webui/tests/reads.test.mjs:21: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:104:1
|
||||
✖ business: missing or invalid file refuses with the module's text, redacted (13.446815ms)
|
||||
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/r54a/mutwt/packages/webui/tests/reads.test.mjs:110: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:139:1
|
||||
✖ settings: release, board, loopback address, notifier binding name only (1725.215019ms)
|
||||
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/r54a/mutwt/packages/webui/tests/reads.test.mjs:155: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:185:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1436.423592ms)
|
||||
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/r54a/mutwt/packages/webui/tests/views.test.mjs:208: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 0443a7c9..f58e295c 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -175,7 +175,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 (4200.011825ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4287.337505ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6767.800204ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (279.383429ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (127.629469ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (177.380594ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3110.092035ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (216.322083ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.688862ms)
|
||||
✔ serve refuses a --business value that is not a business id (257.348378ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4187.488658ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2597.873871ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1319.75687ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1524.467611ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4247.752749ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55468.239155ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.993404ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.237632ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1448.356706ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1243.781784ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (29.010056ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (87.088738ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (47.891683ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1289.868745ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4184.66885ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23528.884292ms)
|
||||
✔ loopback host and board origin fail closed (9.164883ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (139.400051ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (269.611326ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2210.316145ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.225827ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.306369ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.912981ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7919.078486ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2726.485488ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5182.080981ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1701.612976ms)
|
||||
✔ the server log names no config path when the system config refuses (262.023568ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55954.757552
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:40:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (1448.356706ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at /srv/secret/x.token for 523456789012345678'
|
||||
- 'token at <path> for <id>'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:49: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',
|
||||
expected: 'token at <path> for <id>',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at tests/views.test.mjs:185:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1701.612976ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries id 523456789012345678
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads-fixture.mjs:53:38)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:191:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:192: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 0443a7c9..bb2c4125 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,
|
||||
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 (4570.193723ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4956.198068ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (7107.58317ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (769.948386ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (494.675929ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (136.265847ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2443.327434ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (258.531965ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.502105ms)
|
||||
✔ serve refuses a --business value that is not a business id (212.783705ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4993.067816ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2996.990459ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1631.875166ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1186.796127ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4696.661508ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55729.545624ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.903061ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.268462ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2515.470013ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (2087.981999ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.103344ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (7.270486ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (12.478573ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (450.947921ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4766.405351ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22539.695656ms)
|
||||
✔ loopback host and board origin fail closed (9.137815ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (159.152836ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (138.985494ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2048.036231ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.242791ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.275387ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.737133ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7486.985895ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2934.077185ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4760.262064ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2859.762634ms)
|
||||
✔ the server log names no config path when the system config refuses (315.570758ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 56412.306795
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:185:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2859.762634ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'token at /srv/secret/x.token for 523456789012345678'
|
||||
- 'token at <path> for <id>'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:194: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',
|
||||
expected: 'token at <path> for <id>',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 0443a7c9..3b684c08 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -147,7 +147,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,88 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4709.018453ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4973.594263ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5094.682703ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (221.296591ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (85.930486ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (42.082383ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2307.10047ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (199.49397ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.962067ms)
|
||||
✔ serve refuses a --business value that is not a business id (185.701252ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4615.784771ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1847.441235ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1159.459305ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1340.222303ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4778.356902ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53978.621659ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.659993ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.223706ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1277.587507ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (913.900924ms)
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (37.45811ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (31.583646ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (31.72574ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1476.970439ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4756.782144ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24948.382349ms)
|
||||
✔ loopback host and board origin fail closed (7.539573ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (119.828281ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (92.075561ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1064.918332ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.288778ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.412184ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.387512ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7389.030034ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2512.855214ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5019.358561ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1469.499886ms)
|
||||
✔ the server log names no config path when the system config refuses (264.214792ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54385.798876
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:78:1
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (37.45811ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries discordUserId
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads-fixture.mjs:54:185)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:83: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:185:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1469.499886ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries discordUserId
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads-fixture.mjs:54:185)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:191:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:198: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 0443a7c9..8d7820f1 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -138,7 +138,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,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4004.937464ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3917.400112ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5170.572708ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (287.407303ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (166.167279ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (199.702392ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3138.725814ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (115.537443ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.728165ms)
|
||||
✔ serve refuses a --business value that is not a business id (238.234487ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3957.452635ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2434.993651ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1218.814393ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1584.178275ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3973.306995ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55015.00821ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.86278ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.292161ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2322.265964ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1114.018863ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (9.728956ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (15.795049ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (41.263849ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (476.586387ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3957.622785ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22809.958545ms)
|
||||
✔ loopback host and board origin fail closed (9.47624ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (129.714901ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (123.675161ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2294.81738ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.880216ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.110731ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.724916ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7477.226767ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2740.634144ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5141.920321ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1610.387559ms)
|
||||
✔ the server log names no config path when the system config refuses (212.181681ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55625.222535
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 0443a7c9..4357ec46 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -124,7 +124,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,88 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4320.342173ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4861.262698ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6014.528276ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (240.14141ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (109.720314ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (58.088948ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2366.690803ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (347.421521ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (536.340319ms)
|
||||
✔ serve refuses a --business value that is not a business id (244.651155ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4525.098097ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2537.083859ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1372.734646ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1514.772455ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4156.771288ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54196.838627ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.046309ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.502866ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1253.34351ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (851.611323ms)
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (30.771802ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (17.083256ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (52.61554ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (907.907753ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4003.394ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (24408.169579ms)
|
||||
✔ loopback host and board origin fail closed (9.25948ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (142.502118ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (112.15472ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1004.917225ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.425495ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.576602ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.512941ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8275.616778ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2614.697551ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5923.260745ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1654.074503ms)
|
||||
✔ the server log names no config path when the system config refuses (213.349984ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54636.24251
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:78:1
|
||||
✖ business: names, vars on the allowlist, authority, credential metadata only (30.771802ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries CODER_GITEA_SECRET_ENV
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads-fixture.mjs:54:185)
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:83: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:185:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1654.074503ms)
|
||||
AssertionError [ERR_ASSERTION]: response carries CODER_GITEA_SECRET_ENV
|
||||
|
||||
true !== false
|
||||
|
||||
at clean (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads-fixture.mjs:54:185)
|
||||
at seen (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:191:30)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:198: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 0443a7c9..2f03f3f5 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -158,7 +158,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,92 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3650.247354ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3826.974177ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6474.565325ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (327.782299ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (205.8335ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (131.381401ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3012.738672ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (180.000411ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.983485ms)
|
||||
✔ serve refuses a --business value that is not a business id (186.706417ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3645.611452ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3438.823899ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1683.525095ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1386.921952ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3688.668232ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54782.584998ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.958002ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.281795ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2451.051865ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1187.512117ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (14.308173ms)
|
||||
✖ business: missing or invalid file refuses with the module's text, redacted (9.423229ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (17.044179ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1785.282944ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3698.089316ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22352.671379ms)
|
||||
✔ loopback host and board origin fail closed (8.954892ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (148.083539ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (375.111628ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1949.171088ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.987791ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.17167ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.739585ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7374.265867ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3370.774154ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5497.49223ms)
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2167.33082ms)
|
||||
✔ the server log names no config path when the system config refuses (266.89509ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 36
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55318.229797
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:104:1
|
||||
✖ business: missing or invalid file refuses with the module's text, redacted (9.423229ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
error: 'not-configured',
|
||||
+ message: 'business file not found: /home/jwoltje/darkwing-scratch/r54a/tmp/mosaic-webui-reads-WGaPEJ/config/businesses/acme.json'
|
||||
- message: 'business file not found: <config>/businesses/acme.json'
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:110: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/r54a/tmp/mosaic-webui-reads-WGaPEJ/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:185:1
|
||||
✖ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2167.33082ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'business file not found: /home/jwoltje/darkwing-scratch/r54a/tmp/mosaic-webui-reads-XOYkdN/config/businesses/acme.json'
|
||||
- 'business file not found: <config>/businesses/acme.json'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:208: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/r54a/tmp/mosaic-webui-reads-XOYkdN/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 0443a7c9..50bfc986 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -197,7 +197,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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4817.858569ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4831.116292ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6438.845053ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (261.893139ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (128.527526ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (357.436891ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3111.192772ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (151.434433ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.197179ms)
|
||||
✔ serve refuses a --business value that is not a business id (178.261532ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3487.673599ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3146.74374ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1816.87862ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1113.619555ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3498.498318ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54569.122714ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.696016ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.252845ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2326.020362ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1045.331492ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (31.836555ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (51.675294ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (22.849902ms)
|
||||
✖ settings: release, board, loopback address, notifier binding name only (1275.177424ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3243.06729ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22482.941715ms)
|
||||
✔ loopback host and board origin fail closed (9.017601ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (141.486246ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (102.08935ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2474.838466ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.186829ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.396715ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.697522ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7569.493062ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2691.59571ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5186.847432ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2597.52527ms)
|
||||
✔ the server log names no config path when the system config refuses (283.944681ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55034.437097
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:139:1
|
||||
✖ settings: release, board, loopback address, notifier binding name only (1275.177424ms)
|
||||
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/r54a/tmp/mosaic-webui-reads-AgJNR1/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/r54a/mutwt/packages/webui/tests/reads.test.mjs:155: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/r54a/tmp/mosaic-webui-reads-AgJNR1/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,71 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3817.703923ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5659.498256ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6439.043369ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (414.481885ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (196.26774ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (433.236761ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2853.910625ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (196.312458ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.937494ms)
|
||||
✔ serve refuses a --business value that is not a business id (186.136437ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3702.914845ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2862.774682ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1422.952668ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1164.889844ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3732.745577ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55078.235119ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.963655ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.272451ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2325.989519ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1363.790238ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (14.618212ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (11.854573ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (22.083734ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1153.399222ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3884.944785ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22223.74894ms)
|
||||
✔ loopback host and board origin fail closed (8.621355ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (143.327015ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (213.530601ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1953.585562ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.062193ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.793912ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.43552ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7690.504357ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2529.808928ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4899.427222ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2906.015306ms)
|
||||
✖ the server log names no config path when the system config refuses (158.108662ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55517.92932
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:220:1
|
||||
✖ the server log names no config path when the system config refuses (158.108662ms)
|
||||
AssertionError [ERR_ASSERTION]: missing: the log names the temporary directory: Mosaic Console: http://127.0.0.1:45003/
|
||||
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/r54a/tmp/mosaic-webui-log-AVrgLO/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/r54a/mutwt/packages/webui/tests/views.test.mjs:232: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 0443a7c9..ab908629 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -77,7 +77,7 @@ export function queueReader({ root, env = process.env, timeoutMs = 30000, maxByt
|
||||
if (status === null) return done(reject, new ReadError('read-failed', 'the queue read did not finish'));
|
||||
// 2 is the store's "invalid data or refused": fail closed. Anything
|
||||
// else failed to run; 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', said || `the queue read exited ${status}`));
|
||||
try {
|
||||
const doc = JSON.parse(Buffer.concat(out).toString('utf8'));
|
||||
@@ -0,0 +1,69 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5000.920903ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6101.070206ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5149.705568ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (202.168278ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (118.869951ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (56.799173ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2185.591574ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (228.176327ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.144008ms)
|
||||
✔ serve refuses a --business value that is not a business id (179.885403ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4383.570339ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2385.381128ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1251.275827ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1299.472932ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4954.067159ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54150.717577ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.875352ms)
|
||||
✔ credential state from the date alone, as the bus states it (1.110811ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1080.450373ms)
|
||||
✖ queue: a refused read fails closed with the store's text, redacted (865.486429ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (33.648624ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (43.174739ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (55.195688ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1735.767622ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4953.621555ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22780.557902ms)
|
||||
✔ loopback host and board origin fail closed (7.991927ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (132.502511ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (103.336956ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (923.407023ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.142534ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.34311ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.607216ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7916.296577ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2821.242953ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5077.761226ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2307.244753ms)
|
||||
✔ the server log names no config path when the system config refuses (212.086109ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54580.470005
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:66:1
|
||||
✖ queue: a refused read fails closed with the store's text, redacted (865.486429ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'read-failed'
|
||||
- 'queue-refused'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:72: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'
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 0443a7c9..738eeabd 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -67,7 +67,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,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3866.059382ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4419.937987ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5405.701111ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (519.485737ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (113.885362ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (81.818732ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2598.472555ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (284.401929ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.464522ms)
|
||||
✔ serve refuses a --business value that is not a business id (176.819738ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3360.350258ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2475.164929ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1548.551466ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1159.760586ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4398.513141ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53904.389573ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.905984ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.466905ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1877.649139ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1017.101102ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (14.700461ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (42.691838ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (11.231969ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (985.174883ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5045.616406ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21943.942895ms)
|
||||
✔ loopback host and board origin fail closed (7.980215ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (152.92514ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (108.918606ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1508.292551ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.226657ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.213199ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.727394ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7770.048483ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2575.003059ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5342.218834ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1475.424402ms)
|
||||
✔ the server log names no config path when the system config refuses (211.232239ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54372.574594
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 0443a7c9..be748ef1 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -64,7 +64,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,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4065.728473ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4748.411013ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5432.64066ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (251.094089ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (152.1088ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (70.226629ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2389.336743ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (150.170513ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.673989ms)
|
||||
✔ serve refuses a --business value that is not a business id (205.809698ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4456.794936ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2219.534804ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1520.224118ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1156.358866ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4544.113781ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53836.532458ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.528531ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.226672ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1277.199973ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1030.143575ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (92.670531ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (46.184315ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (23.83569ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (790.819227ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4452.918008ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22738.303689ms)
|
||||
✔ loopback host and board origin fail closed (7.888341ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (130.305992ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (100.057231ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (989.45021ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.110478ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.307697ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.667219ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7737.043908ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2539.24466ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5385.6383ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2107.620417ms)
|
||||
✔ the server log names no config path when the system config refuses (215.432638ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54219.239493
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/reads.mjs b/packages/webui/src/reads.mjs
|
||||
index 0443a7c9..17fd8dca 100644
|
||||
--- a/packages/webui/src/reads.mjs
|
||||
+++ b/packages/webui/src/reads.mjs
|
||||
@@ -108,7 +108,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,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3985.688283ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4447.990495ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6526.818377ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (231.270755ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (89.655118ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (238.493594ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3141.608774ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (107.72097ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.390433ms)
|
||||
✔ serve refuses a --business value that is not a business id (269.837021ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4330.192204ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2278.387967ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1301.091785ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1173.992175ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4291.22648ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53396.828902ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.899748ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.26954ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2349.273168ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1447.280241ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (12.839479ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (14.813239ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.441831ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (572.775828ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4348.935418ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22560.65201ms)
|
||||
✔ loopback host and board origin fail closed (8.401405ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (115.287665ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (91.329521ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2293.702044ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.2854ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.42369ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.836279ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7773.688678ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2735.667567ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5064.701774ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2616.434555ms)
|
||||
✔ the server log names no config path when the system config refuses (215.619506ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53826.308905
|
||||
@@ -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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3815.689527ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4120.862703ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6446.050679ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (346.888376ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (165.142536ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (197.93156ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2844.510877ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (176.820627ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.116722ms)
|
||||
✔ serve refuses a --business value that is not a business id (281.267378ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3811.106505ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2720.620433ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1522.129375ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (792.600848ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3909.957477ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55212.888172ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.906873ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.302761ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (2388.033224ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1416.198428ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (10.872099ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (11.147121ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (11.425321ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (836.630321ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3928.415346ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22243.032316ms)
|
||||
✔ loopback host and board origin fail closed (7.301129ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (119.905625ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (162.644707ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2049.828497ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.46393ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.928379ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.39511ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7056.354698ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2889.413893ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4816.099964ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2441.977189ms)
|
||||
✔ the server log names no config path when the system config refuses (275.214472ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55832.586028
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:40:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (2388.033224ms)
|
||||
AssertionError [ERR_ASSERTION]: /api/queue
|
||||
|
||||
200 !== 405
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:61: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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4334.088061ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4563.597523ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6981.519232ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (301.49155ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (121.989525ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (85.758336ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3239.318814ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (159.127953ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.138059ms)
|
||||
✔ serve refuses a --business value that is not a business id (280.268697ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4407.36187ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2733.008688ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (2070.930421ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1565.67244ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4194.007266ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55288.825077ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (3.066497ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.285937ms)
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (2508.769179ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1702.717269ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (10.897652ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (13.918497ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (19.626303ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (421.789955ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4360.92173ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22414.895262ms)
|
||||
✔ loopback host and board origin fail closed (8.368719ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (129.667475ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (133.870046ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (2069.33384ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.35725ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.160257ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.057927ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7383.171842ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (3592.513343ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4883.523192ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3931.282217ms)
|
||||
✔ the server log names no config path when the system config refuses (211.720622ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55939.200557
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:40:1
|
||||
✖ queue: rows, one row, ids and methods, notes; no path or id from a row (2508.769179ms)
|
||||
AssertionError [ERR_ASSERTION]: 0
|
||||
|
||||
404 !== 400
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:59: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,69 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4935.828828ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5556.981854ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5798.803355ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (243.736845ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (80.028636ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (73.396795ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2140.799295ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (223.160338ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (546.378813ms)
|
||||
✔ serve refuses a --business value that is not a business id (187.221128ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4872.852956ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2048.103464ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1540.691642ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1200.708193ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5182.555675ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54000.807918ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.045458ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.279761ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1118.151989ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1143.076623ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (21.877954ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (21.429199ms)
|
||||
✖ business without --business: the live bus host, else no-bus-host; no system config: not-configured (36.321892ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1138.749147ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4776.151142ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22916.479064ms)
|
||||
✔ loopback host and board origin fail closed (9.799039ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (125.929636ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (90.292384ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (971.714773ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.033591ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.367726ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.902784ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7534.182322ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2843.620239ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5608.585604ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1701.527913ms)
|
||||
✔ the server log names no config path when the system config refuses (324.537747ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54511.288835
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/reads.test.mjs:126:1
|
||||
✖ business without --business: the live bus host, else no-bus-host; no system config: not-configured (36.321892ms)
|
||||
AssertionError [ERR_ASSERTION]: /api/business
|
||||
+ actual - expected
|
||||
|
||||
+ 'invalid-response'
|
||||
- 'not-configured'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/reads.test.mjs:136: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 ef13c793..20f2c620 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -488,7 +488,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,58 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5437.41818ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5425.885595ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5405.715959ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (250.174144ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (101.189068ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (84.11427ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2250.76167ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (180.702079ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.226821ms)
|
||||
✔ serve refuses a --business value that is not a business id (172.053203ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4797.019702ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1812.125303ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1072.161095ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (977.720549ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5360.236268ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54038.945666ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.900312ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.329717ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1084.161379ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (813.368263ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (23.033284ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (39.320861ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (89.915802ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (824.973907ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5421.603398ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23092.309356ms)
|
||||
✔ loopback host and board origin fail closed (9.341519ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (161.875401ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (129.189318ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1115.164199ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.273333ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.491156ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.870097ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8029.584162ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2562.190099ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (13061.411383ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1195.131788ms)
|
||||
✔ the server log names no config path when the system config refuses (211.973308ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54452.502364
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (13061.411383ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: !!document.querySelector("#bus-view .banner.ref"): undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r54a/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/r54a/mutwt/packages/webui/tests/views.test.mjs:107: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 ef13c793..3d33625f 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -563,7 +563,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,67 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5359.138834ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5574.687643ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6394.275362ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (400.569442ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (335.69022ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (170.26143ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2948.747983ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (559.020686ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (655.789471ms)
|
||||
✔ serve refuses a --business value that is not a business id (175.846302ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5298.811007ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1890.466511ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1275.488352ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (821.434352ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4194.197709ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55288.083268ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.005061ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.303587ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2465.438021ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1303.981495ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (11.316386ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (16.125405ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (12.424481ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1625.559026ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4050.738206ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22337.434477ms)
|
||||
✔ loopback host and board origin fail closed (8.803767ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (117.180827ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (520.6679ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1881.24091ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.537959ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.453414ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.732381ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7317.414925ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2843.296748ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5449.283824ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1747.082479ms)
|
||||
✔ the server log names no config path when the system config refuses (267.75453ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55852.086444
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (5449.283824ms)
|
||||
AssertionError [ERR_ASSERTION]: #/settings
|
||||
|
||||
0 !== 2
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r54a/mutwt/packages/webui/tests/views.test.mjs:114: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 ef13c793..5eae9ad7 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -624,7 +624,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,58 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5084.508493ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5458.239348ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5740.696949ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (386.426731ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (97.982497ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (74.793341ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2334.988593ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (271.395983ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (538.048771ms)
|
||||
✔ serve refuses a --business value that is not a business id (235.772279ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4998.478789ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1842.085999ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1126.877575ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1030.515436ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4090.593159ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (44422.737955ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.404832ms)
|
||||
✔ credential state from the date alone, as the bus states it (3.015192ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1584.305698ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1044.686503ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (32.366721ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (44.982778ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (62.791652ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1038.015137ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4742.884676ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23522.751503ms)
|
||||
✔ loopback host and board origin fail closed (11.47803ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (142.545877ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (98.818306ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1215.200025ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.712102ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.277194ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.035858ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8498.730424ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2527.827632ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15632.85447ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1206.864572ms)
|
||||
✔ the server log names no config path when the system config refuses (211.188726ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 44945.766974
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (15632.85447ms)
|
||||
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/r54a/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/r54a/mutwt/packages/webui/tests/views.test.mjs:167:34)
|
||||
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 ef13c793..9cc485c7 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -529,7 +529,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,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3208.32569ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4897.875723ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5614.816105ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (310.158448ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (468.360126ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (72.552986ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2650.653791ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (157.502233ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.96326ms)
|
||||
✔ serve refuses a --business value that is not a business id (194.43599ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3091.904095ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2969.867887ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1296.565486ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1173.616968ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4948.108811ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53770.941289ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.081629ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.257325ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2362.77099ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (799.047262ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (33.870027ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (39.487155ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (23.133068ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1099.603118ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4997.9674ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22093.780558ms)
|
||||
✔ loopback host and board origin fail closed (8.135877ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (167.400008ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (223.31773ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1919.691821ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.379393ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.515625ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.892738ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7173.088852ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2568.806432ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4733.117204ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1919.786753ms)
|
||||
✔ the server log names no config path when the system config refuses (212.79365ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54905.986885
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/webui/src/public/bus.js b/packages/webui/src/public/bus.js
|
||||
index ef13c793..86d63a83 100644
|
||||
--- a/packages/webui/src/public/bus.js
|
||||
+++ b/packages/webui/src/public/bus.js
|
||||
@@ -667,7 +667,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,58 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4120.956867ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4824.216209ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6289.244588ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (206.539641ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (305.656532ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (364.895813ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (3091.114734ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (377.459739ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.188869ms)
|
||||
✔ serve refuses a --business value that is not a business id (181.986594ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3680.756898ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2693.823091ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1130.580172ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1135.919719ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3711.979432ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55493.606219ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.108104ms)
|
||||
✔ credential state from the date alone, as the bus states it (2.57012ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (2317.660531ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1326.760192ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (14.91271ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (13.924507ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (34.544246ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (958.427757ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3741.282376ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22397.184221ms)
|
||||
✔ loopback host and board origin fail closed (8.88274ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (119.436011ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (78.584529ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1912.139073ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.29965ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.467233ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.940463ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7505.57196ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2711.484056ms)
|
||||
✖ Queue, Business and Settings in their five states, with no move button (16338.942804ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (5582.819377ms)
|
||||
✔ the server log names no config path when the system config refuses (326.046882ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 37
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55934.909432
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/views.test.mjs:63:1
|
||||
✖ Queue, Business and Settings in their five states, with no move button (16338.942804ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: document.querySelector("#mode").value==="dark": undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r54a/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/r54a/mutwt/packages/webui/tests/views.test.mjs:177: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 (1213.513287ms)
|
||||
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1069.107357ms)
|
||||
ℹ 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 (974.487379ms)
|
||||
ℹ 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 (1116.046618ms)
|
||||
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1026.511233ms)
|
||||
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1006.880591ms)
|
||||
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1385.680207ms)
|
||||
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1026.268035ms)
|
||||
✔ F1: a shared-index change during the procedure is not committed (1107.139632ms)
|
||||
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1064.514494ms)
|
||||
✔ F1: a missing or a different hook refuses (794.853144ms)
|
||||
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1241.997413ms)
|
||||
✔ F1: the canary refuses a hook that git would not run (733.099779ms)
|
||||
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1139.731222ms)
|
||||
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (903.361989ms)
|
||||
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (926.505232ms)
|
||||
✔ bootstrap: the archived tests and validator run outside any repository (907.817519ms)
|
||||
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (921.470664ms)
|
||||
✔ general: a queue write after the snapshot is not committed (1352.817484ms)
|
||||
✔ general: a snapshot whose log does not extend the base refuses (1111.754128ms)
|
||||
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (162.258953ms)
|
||||
✔ general: environment overrides, a linked worktree and usage (667.207031ms)
|
||||
✔ general: a queue path staged before the run refuses at step 1 (682.311835ms)
|
||||
✔ general: HEAD's queue tests failing in the archive refuse (894.163937ms)
|
||||
✔ genesis document serializes deterministically and replays (3.72264ms)
|
||||
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.261016ms)
|
||||
✔ a tampered result, receipt or viewSha fails replay (2.069395ms)
|
||||
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.156196ms)
|
||||
✔ add: defaults for an ordinary seat, privileged extras, refusals (3.698885ms)
|
||||
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (4.833452ms)
|
||||
✔ matrix: release, review round, changes requested and waiting-on-jason (13.459417ms)
|
||||
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (8.120048ms)
|
||||
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (18.90493ms)
|
||||
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.21006ms)
|
||||
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1311.201423ms)
|
||||
✔ matrix: blocked keeps the claim and returns only to previousState (3.468986ms)
|
||||
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.267256ms)
|
||||
✔ field edits: who may change what (4.188321ms)
|
||||
✔ set issues keeps a logged narrowing of closes (N10) (2.572373ms)
|
||||
✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.587104ms)
|
||||
✔ every accepted text renders to nine cells on every row (N8) (19.691247ms)
|
||||
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.486984ms)
|
||||
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.40233ms)
|
||||
✔ replay holds every op id to the caller's rule (N11) (4.722269ms)
|
||||
✔ note: owner, listed reviewer or privileged; empty clears (1.00409ms)
|
||||
✔ assign moves the claim with the owner; done clears it (2.164224ms)
|
||||
✔ render is byte-stable and escapes pipes (0.458507ms)
|
||||
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.520792ms)
|
||||
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (15.736859ms)
|
||||
✔ canonical args make a retry's identity independent of list order (0.294223ms)
|
||||
✔ manifests, headings and blob ids (0.416495ms)
|
||||
✔ the migration map: one queue-map block, exact keys (0.528395ms)
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (558.04744ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (181.075919ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.458276ms)
|
||||
✔ acquire publishes the record by link; release removes only its own lock (10.631557ms)
|
||||
✔ a kill between the temp write and the link leaves no lock (54.98358ms)
|
||||
✔ a short or failed temp write refuses and leaves no lock and no temp (9.046649ms)
|
||||
✔ a link error other than EEXIST refuses (6.03194ms)
|
||||
✔ an error after the link releases the lock: unreadable gate, failing temp stat (25.058842ms)
|
||||
✔ a release that fails on a gate path is reported, never a stack trace (P1) (37.873526ms)
|
||||
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10097.430167ms)
|
||||
✔ two concurrent unlockers: the second refuses on the gate (32.774663ms)
|
||||
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (9.917845ms)
|
||||
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (11.895493ms)
|
||||
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (25.265671ms)
|
||||
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (22.564019ms)
|
||||
✔ the same pid and start on a different boot is mismatch (0.467676ms)
|
||||
✔ a foreign host is unknown whatever the local pid says; unlock refuses (70.360828ms)
|
||||
✔ unreadable /proc: classification is unknown and acquire refuses (0.652675ms)
|
||||
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.540424ms)
|
||||
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (24.872192ms)
|
||||
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (32.477596ms)
|
||||
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (13.653148ms)
|
||||
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (60.577196ms)
|
||||
✔ the migration map validates and renders the golden genesis table (3.280246ms)
|
||||
✔ the marked QUEUE.md holds every row and parked item between its markers (1.587567ms)
|
||||
✔ map-check reports each kind of drift (5.023352ms)
|
||||
✔ a request posts once as the requester; a retry sends nothing (776.579847ms)
|
||||
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (4441.347113ms)
|
||||
✔ the pre-send checks: GET user must name the requester, under the deadline (1570.572909ms)
|
||||
✔ the lead's request refuses a token for login sage (1060.099198ms)
|
||||
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (974.653704ms)
|
||||
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2565.280814ms)
|
||||
✔ a same-op retry after a kill sends nothing, even with a stale view (3502.362113ms)
|
||||
✔ a held lock at the outcome exits 3 and names what the transport said (772.010489ms)
|
||||
✔ late outcomes: after an abandon, and after a resolve with the same or another id (2455.150991ms)
|
||||
✔ resolve checks the comment: issue, markers, round, candidate and author (1780.398496ms)
|
||||
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (811.227356ms)
|
||||
✔ validateRow checks a request round's shape, which every replayed entry must keep (518.426737ms)
|
||||
✔ request, changes, a new candidate, approval: every round pinned; no review files (1835.462759ms)
|
||||
✔ a row with no reviewers opens a round that posts nothing (1232.619181ms)
|
||||
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1362.828077ms)
|
||||
✔ semantics: v1 entries replay as before; review entries need v2 (505.983018ms)
|
||||
✔ set reviewers refuses the row's owner (2026-09-28) (381.747755ms)
|
||||
✔ the owner records no verdict, even as a listed reviewer (535.840723ms)
|
||||
✔ a request comment over the length limit is not sent (633.418807ms)
|
||||
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (695.947063ms)
|
||||
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2665.868291ms)
|
||||
✔ genesis: refusals before anything is written (399.556692ms)
|
||||
✔ genesis: the map must be committed, well formed, with committed briefs and seats (593.065097ms)
|
||||
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (565.934383ms)
|
||||
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (381.577139ms)
|
||||
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (644.239906ms)
|
||||
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (769.483708ms)
|
||||
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (762.62937ms)
|
||||
✔ a retried add returns the id it first allocated, after reassignment and after done (962.158705ms)
|
||||
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (703.083043ms)
|
||||
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1260.087325ms)
|
||||
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (926.025142ms)
|
||||
✔ add, set reviewers and assign refuse the row's owner as a reviewer (611.721552ms)
|
||||
✔ the working-brief check: a changed working copy refuses the start and flags next (688.832413ms)
|
||||
✔ next: resume first, then nothing for an idle seat; needs a seat (302.049161ms)
|
||||
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (755.381882ms)
|
||||
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1320.096998ms)
|
||||
✔ a hand edit to queue.json refuses every verb, reads included (623.985474ms)
|
||||
✔ verify and render --check leave bytes and mtimes unchanged (456.693689ms)
|
||||
✔ render is byte-stable across runs and repositories (323.632045ms)
|
||||
✔ snapshot and verify --snapshot (853.89672ms)
|
||||
✔ usage errors exit 4 (578.558636ms)
|
||||
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (287.804763ms)
|
||||
✔ a rename failure: nothing visible, temp removed (160.964047ms)
|
||||
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (221.921063ms)
|
||||
✔ a directory fsync failure, then sync names the op (384.379841ms)
|
||||
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (229.818869ms)
|
||||
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (206.280197ms)
|
||||
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (237.081979ms)
|
||||
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (192.959497ms)
|
||||
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (201.519196ms)
|
||||
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (192.012053ms)
|
||||
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (152.234406ms)
|
||||
✔ a view write that fails keeps the op and reports a stale view (181.103512ms)
|
||||
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (676.48917ms)
|
||||
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (709.803644ms)
|
||||
✔ SIGKILL after the witness, before the view: the stale refusal names the op (627.947435ms)
|
||||
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (676.296653ms)
|
||||
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (217.160751ms)
|
||||
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1043.283064ms)
|
||||
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (333.644697ms)
|
||||
✔ a header edit during a write: the op stands, the view write is skipped with a warning (227.120825ms)
|
||||
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (222.117391ms)
|
||||
✖ rows: every row as show prints it, in list order; writes nothing; same notes and refusals as list (286.928512ms)
|
||||
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (282.274709ms)
|
||||
✔ a writer paused before and after the witness rename: readers see a tail, then a match (211.829398ms)
|
||||
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (609.130001ms)
|
||||
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (653.178306ms)
|
||||
✔ the platform check refuses other filesystems (131.913302ms)
|
||||
✔ tmpfs passes only a test layer that allows it (N5) (318.184031ms)
|
||||
✔ unlock keeps a multi-line lock record on stdout (P3) (250.143797ms)
|
||||
ℹ tests 149
|
||||
ℹ suites 0
|
||||
ℹ pass 148
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 31150.650186
|
||||
|
||||
✖ 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 (286.928512ms)
|
||||
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/r54a/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,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,53 @@
|
||||
# Row 54 round 1 mutants: 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"
|
||||
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 };")],
|
||||
}
|
||||
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/r54a/tmp
|
||||
S=~/darkwing-scratch/r54a; M=$S/r1/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"\"(D\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,34 @@
|
||||
start 2026-10-10T20:09:04Z
|
||||
D01 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D02 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D03 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D04 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D05 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D06 killed exit=1 ℹ pass 36 ℹ fail 2
|
||||
D07 killed exit=1 ℹ pass 34 ℹ fail 4
|
||||
D08 killed exit=1 ℹ pass 35 ℹ fail 3
|
||||
D09 killed exit=1 ℹ pass 36 ℹ fail 2
|
||||
D10 killed exit=1 ℹ pass 34 ℹ fail 4
|
||||
D11 killed exit=1 ℹ pass 36 ℹ fail 2
|
||||
D12 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D13 killed exit=1 ℹ pass 36 ℹ fail 2
|
||||
D14 SURVIVED exit=0 ℹ pass 38 ℹ fail 0
|
||||
D15 killed exit=1 ℹ pass 36 ℹ fail 2
|
||||
D16 killed exit=1 ℹ pass 36 ℹ fail 2
|
||||
D17 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D18 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D19 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D20 SURVIVED exit=0 ℹ pass 38 ℹ fail 0
|
||||
D21 SURVIVED exit=0 ℹ pass 38 ℹ fail 0
|
||||
D22 SURVIVED exit=0 ℹ pass 38 ℹ fail 0
|
||||
D23 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D24 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D25 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D26 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D27 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D28 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D29 SURVIVED exit=0 ℹ pass 38 ℹ fail 0
|
||||
D30 killed exit=1 ℹ pass 37 ℹ fail 1
|
||||
D31 killed exit=1 ℹ pass 148 ℹ fail 1
|
||||
end 2026-10-10T20:37:00Z
|
||||
DONE
|
||||
@@ -0,0 +1 @@
|
||||
tokens.css is current
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (2.249977ms)
|
||||
✔ the fixture business validates and comes back frozen (4.532959ms)
|
||||
✔ two instances may share a definition (1.765642ms)
|
||||
✔ top-level refusals (4.302103ms)
|
||||
✔ arbiters and projects (7.247986ms)
|
||||
✔ role instances (3.272868ms)
|
||||
✔ Vikunja bots (10.504188ms)
|
||||
✔ a role without Vikunja takes no tracker block (2.672516ms)
|
||||
✔ credential references match the definition's services (3.118138ms)
|
||||
✔ launch (11.412474ms)
|
||||
✔ loadBusiness: file checks (2.813378ms)
|
||||
✔ loadBusiness: not a regular file (44.568346ms)
|
||||
✔ loading writes nothing (1.108792ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.75627ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.698687ms)
|
||||
✔ usage errors exit 4 (309.603048ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (66.400709ms)
|
||||
✔ validate: project files (320.30546ms)
|
||||
✔ validate: missing files and a broken system config (249.861145ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (87.767791ms)
|
||||
✔ validate: a token file inside the repository is refused (117.810768ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (243.797067ms)
|
||||
✔ resolve: prints one instance's record (268.027885ms)
|
||||
✔ resolve: refusals (504.503503ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (3.825708ms)
|
||||
✔ check: a good file has no problems (0.837736ms)
|
||||
✔ check never opens the file: a write-only token passes (0.329552ms)
|
||||
✔ check: file problems (0.839735ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.828104ms)
|
||||
✔ check: dates and environment references (0.405191ms)
|
||||
✔ path and load (3.281625ms)
|
||||
✔ refusals (1.710463ms)
|
||||
✔ systemVars flattens the validated config (2.908558ms)
|
||||
✔ precedence: system, business, project, project role, agent (7.104187ms)
|
||||
✔ limits narrow the definition and never widen it (2.116209ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (5.209865ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (1.785323ms)
|
||||
✔ limits.authority narrows cross-role actions too (1.147422ms)
|
||||
✔ classify (1.173259ms)
|
||||
✔ the record carries what the broker and launcher need (0.983132ms)
|
||||
✔ digest: key order doesn't matter, any value change does (6.76918ms)
|
||||
✔ refusals (2.209809ms)
|
||||
✔ the four shipped version 2 roles load (4.29083ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.426069ms)
|
||||
✔ shipped authority follows the note's table (0.727498ms)
|
||||
✔ version 1 files keep loading with no authority (1.343343ms)
|
||||
✔ the conductor policy isn't a role (0.279846ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.526959ms)
|
||||
✔ version 2 refusals (1.714782ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (2.951528ms)
|
||||
✔ credentials: Gitea scopes (1.137161ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.59683ms)
|
||||
✔ credentials: services (0.90866ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (0.945123ms)
|
||||
✔ every key names known layers and a merge rule (0.953922ms)
|
||||
✔ unknown keys and wrong layers refuse (0.737586ms)
|
||||
✔ types (1.882153ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.298593ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.390772ms)
|
||||
✔ merge doesn't change its inputs (0.149627ms)
|
||||
ℹ tests 60
|
||||
ℹ suites 0
|
||||
ℹ pass 60
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2240.424879
|
||||
@@ -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 (1582.217933ms)
|
||||
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1243.634971ms)
|
||||
ℹ 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 (1307.636454ms)
|
||||
ℹ 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 (1466.728544ms)
|
||||
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1449.863825ms)
|
||||
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1331.734065ms)
|
||||
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1769.141616ms)
|
||||
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1318.314026ms)
|
||||
✔ F1: a shared-index change during the procedure is not committed (1332.788746ms)
|
||||
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1539.398665ms)
|
||||
✔ F1: a missing or a different hook refuses (1044.492118ms)
|
||||
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1475.985527ms)
|
||||
✔ F1: the canary refuses a hook that git would not run (845.399413ms)
|
||||
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1133.113106ms)
|
||||
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (1009.29169ms)
|
||||
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (1206.049891ms)
|
||||
✔ bootstrap: the archived tests and validator run outside any repository (1101.54087ms)
|
||||
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (1114.500626ms)
|
||||
✔ general: a queue write after the snapshot is not committed (1578.962692ms)
|
||||
✔ general: a snapshot whose log does not extend the base refuses (1178.914348ms)
|
||||
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (188.536771ms)
|
||||
✔ general: environment overrides, a linked worktree and usage (789.753089ms)
|
||||
✔ general: a queue path staged before the run refuses at step 1 (720.416708ms)
|
||||
✔ general: HEAD's queue tests failing in the archive refuse (1049.480391ms)
|
||||
✔ genesis document serializes deterministically and replays (5.850242ms)
|
||||
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (4.131412ms)
|
||||
✔ a tampered result, receipt or viewSha fails replay (3.479977ms)
|
||||
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.289694ms)
|
||||
✔ add: defaults for an ordinary seat, privileged extras, refusals (6.060088ms)
|
||||
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (7.697249ms)
|
||||
✔ matrix: release, review round, changes requested and waiting-on-jason (19.830649ms)
|
||||
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (10.176003ms)
|
||||
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (22.710341ms)
|
||||
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.561966ms)
|
||||
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1715.290938ms)
|
||||
✔ matrix: blocked keeps the claim and returns only to previousState (3.688012ms)
|
||||
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.471631ms)
|
||||
✔ field edits: who may change what (5.919518ms)
|
||||
✔ set issues keeps a logged narrowing of closes (N10) (2.745535ms)
|
||||
✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.834375ms)
|
||||
✔ every accepted text renders to nine cells on every row (N8) (23.225795ms)
|
||||
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.521975ms)
|
||||
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.656362ms)
|
||||
✔ replay holds every op id to the caller's rule (N11) (5.116922ms)
|
||||
✔ note: owner, listed reviewer or privileged; empty clears (1.171954ms)
|
||||
✔ assign moves the claim with the owner; done clears it (2.580929ms)
|
||||
✔ render is byte-stable and escapes pipes (0.767339ms)
|
||||
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.619389ms)
|
||||
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (19.292108ms)
|
||||
✔ canonical args make a retry's identity independent of list order (0.334897ms)
|
||||
✔ manifests, headings and blob ids (0.512792ms)
|
||||
✔ the migration map: one queue-map block, exact keys (0.72184ms)
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (755.581607ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (239.070561ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.463753ms)
|
||||
✔ acquire publishes the record by link; release removes only its own lock (13.725696ms)
|
||||
✔ a kill between the temp write and the link leaves no lock (63.786428ms)
|
||||
✔ a short or failed temp write refuses and leaves no lock and no temp (7.248293ms)
|
||||
✔ a link error other than EEXIST refuses (9.332931ms)
|
||||
✔ an error after the link releases the lock: unreadable gate, failing temp stat (19.805095ms)
|
||||
✔ a release that fails on a gate path is reported, never a stack trace (P1) (59.716098ms)
|
||||
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10129.786417ms)
|
||||
✔ two concurrent unlockers: the second refuses on the gate (70.378457ms)
|
||||
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (33.78472ms)
|
||||
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (44.762526ms)
|
||||
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (41.270041ms)
|
||||
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (21.309492ms)
|
||||
✔ the same pid and start on a different boot is mismatch (1.974589ms)
|
||||
✔ a foreign host is unknown whatever the local pid says; unlock refuses (113.259974ms)
|
||||
✔ unreadable /proc: classification is unknown and acquire refuses (1.031732ms)
|
||||
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.30246ms)
|
||||
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (28.056769ms)
|
||||
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (54.661063ms)
|
||||
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (9.802189ms)
|
||||
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (48.357976ms)
|
||||
✔ the migration map validates and renders the golden genesis table (4.149216ms)
|
||||
✔ the marked QUEUE.md holds every row and parked item between its markers (2.255405ms)
|
||||
✔ map-check reports each kind of drift (5.936848ms)
|
||||
✔ a request posts once as the requester; a retry sends nothing (1091.194918ms)
|
||||
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (5607.310149ms)
|
||||
✔ the pre-send checks: GET user must name the requester, under the deadline (2230.578378ms)
|
||||
✔ the lead's request refuses a token for login sage (1332.927063ms)
|
||||
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (1325.273979ms)
|
||||
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (3417.324146ms)
|
||||
✔ a same-op retry after a kill sends nothing, even with a stale view (3985.134768ms)
|
||||
✔ a held lock at the outcome exits 3 and names what the transport said (795.443994ms)
|
||||
✔ late outcomes: after an abandon, and after a resolve with the same or another id (3149.311768ms)
|
||||
✔ resolve checks the comment: issue, markers, round, candidate and author (2019.64599ms)
|
||||
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (855.267378ms)
|
||||
✔ validateRow checks a request round's shape, which every replayed entry must keep (577.49109ms)
|
||||
✔ request, changes, a new candidate, approval: every round pinned; no review files (2055.811499ms)
|
||||
✔ a row with no reviewers opens a round that posts nothing (1539.080552ms)
|
||||
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1569.317305ms)
|
||||
✔ semantics: v1 entries replay as before; review entries need v2 (629.911317ms)
|
||||
✔ set reviewers refuses the row's owner (2026-09-28) (549.913259ms)
|
||||
✔ the owner records no verdict, even as a listed reviewer (579.334645ms)
|
||||
✔ a request comment over the length limit is not sent (668.113143ms)
|
||||
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (691.457831ms)
|
||||
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (3083.330206ms)
|
||||
✔ genesis: refusals before anything is written (503.286821ms)
|
||||
✔ genesis: the map must be committed, well formed, with committed briefs and seats (807.224701ms)
|
||||
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (642.697502ms)
|
||||
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (496.925873ms)
|
||||
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (751.509475ms)
|
||||
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (1104.107929ms)
|
||||
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (1032.843906ms)
|
||||
✔ a retried add returns the id it first allocated, after reassignment and after done (1280.03922ms)
|
||||
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (1013.810301ms)
|
||||
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1770.540186ms)
|
||||
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (1107.169211ms)
|
||||
✔ add, set reviewers and assign refuse the row's owner as a reviewer (743.959607ms)
|
||||
✔ the working-brief check: a changed working copy refuses the start and flags next (771.199602ms)
|
||||
✔ next: resume first, then nothing for an idle seat; needs a seat (349.575374ms)
|
||||
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (1041.998524ms)
|
||||
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1637.483676ms)
|
||||
✔ a hand edit to queue.json refuses every verb, reads included (825.767395ms)
|
||||
✔ verify and render --check leave bytes and mtimes unchanged (520.027665ms)
|
||||
✔ render is byte-stable across runs and repositories (449.181744ms)
|
||||
✔ snapshot and verify --snapshot (968.801434ms)
|
||||
✔ usage errors exit 4 (598.943632ms)
|
||||
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (415.053673ms)
|
||||
✔ a rename failure: nothing visible, temp removed (228.783561ms)
|
||||
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (281.646422ms)
|
||||
✔ a directory fsync failure, then sync names the op (465.133245ms)
|
||||
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (249.137788ms)
|
||||
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (240.58536ms)
|
||||
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (253.025834ms)
|
||||
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (252.049306ms)
|
||||
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (264.865376ms)
|
||||
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (267.401478ms)
|
||||
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (185.671145ms)
|
||||
✔ a view write that fails keeps the op and reports a stale view (247.337462ms)
|
||||
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (801.261529ms)
|
||||
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (910.629511ms)
|
||||
✔ SIGKILL after the witness, before the view: the stale refusal names the op (732.124673ms)
|
||||
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (806.39669ms)
|
||||
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (366.39408ms)
|
||||
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1462.024959ms)
|
||||
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (671.590028ms)
|
||||
✔ a header edit during a write: the op stands, the view write is skipped with a warning (320.642346ms)
|
||||
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (293.522218ms)
|
||||
✔ rows: every row as show prints it, in list order; writes nothing; same notes and refusals as list (338.132281ms)
|
||||
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (323.527408ms)
|
||||
✔ a writer paused before and after the witness rename: readers see a tail, then a match (346.577018ms)
|
||||
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (675.109699ms)
|
||||
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (681.300682ms)
|
||||
✔ the platform check refuses other filesystems (207.116173ms)
|
||||
✔ tmpfs passes only a test layer that allows it (N5) (432.118831ms)
|
||||
✔ unlock keeps a multi-line lock record on stdout (P3) (252.882648ms)
|
||||
ℹ tests 149
|
||||
ℹ suites 0
|
||||
ℹ pass 149
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 37844.810913
|
||||
@@ -0,0 +1,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4195.060555ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5313.351014ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5832.509445ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (262.208263ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (96.137203ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (75.617629ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2402.204951ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (261.004396ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (531.06761ms)
|
||||
✔ serve refuses a --business value that is not a business id (219.496891ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4264.028355ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2539.115102ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1134.530704ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1038.970715ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4723.291602ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54477.701456ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (5.867895ms)
|
||||
✔ credential state from the date alone, as the bus states it (4.11703ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1464.3776ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (767.146119ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (18.424716ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (61.182638ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (22.557023ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (1067.013458ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4212.447048ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23264.718506ms)
|
||||
✔ loopback host and board origin fail closed (35.569514ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (172.362614ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (94.071947ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1133.747713ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (2.210876ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.976132ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (2.086146ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (8126.602491ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2741.9107ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (5682.218013ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1554.959822ms)
|
||||
✔ the server log names no config path when the system config refuses (265.736239ms)
|
||||
ℹ tests 38
|
||||
ℹ suites 0
|
||||
ℹ pass 38
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55123.86591
|
||||
@@ -0,0 +1,16 @@
|
||||
start 2026-10-10T20:05:27Z
|
||||
node-webui exit=0 ℹ pass 38 ℹ fail 0
|
||||
node-queue exit=0 ℹ pass 149 ℹ fail 0
|
||||
node-business exit=0 ℹ pass 60 ℹ fail 0
|
||||
build-tokens --check exit=0 tokens.css is current
|
||||
test-auth exit=0 selftest: 15 passed, 0 failed
|
||||
test-conductor exit=0 selftest: 17 passed, 0 failed
|
||||
test-config exit=0 selftest: 24 passed, 0 failed
|
||||
test-discord exit=0 discord suite: 66 passed, 0 failed
|
||||
test-extension-package exit=0 extension package selftest: 18 passed, 0 failed
|
||||
test-foundation exit=0 selftest: 44 passed, 0 failed
|
||||
test-queue exit=0 queue suite: 27 passed, 0 failed
|
||||
test-release exit=0 selftest: 4 passed, 0 failed
|
||||
test-task exit=0 selftest: 26 passed, 0 failed
|
||||
end 2026-10-10T20:08:51Z
|
||||
DONE
|
||||
@@ -0,0 +1,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,23 @@
|
||||
On branch refactor
|
||||
Your branch is up to date with 'origin/refactor'.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,26 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 24 passed, 0 failed
|
||||
@@ -0,0 +1,70 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
|
||||
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
|
||||
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
|
||||
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
|
||||
OK real pi with protocol vault adds reserve_id to the git verbs
|
||||
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
|
||||
OK real pi refuses a git key on a read-only root (fail closed)
|
||||
OK real pi with the pilot flags (--no-tools) exposes no tool at all
|
||||
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 178)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
|
||||
discord suite: 66 passed, 0 failed
|
||||
@@ -0,0 +1,21 @@
|
||||
OK initial ordinary-file install
|
||||
OK installed tree matches canonical source
|
||||
OK installed tree has no symlinks
|
||||
OK check detects installation drift
|
||||
OK sync refuses to overwrite installation drift
|
||||
OK check detects an extra destination file
|
||||
OK check detects an extra destination directory
|
||||
OK check rejects a destination symlink
|
||||
OK sync accepts a canonical source update
|
||||
OK updated installation matches canonical source
|
||||
scripts/test-extension-package.sh: line 14: 602290 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
OK sync succeeds after interruption recovery
|
||||
OK unlocked stale lock file does not block
|
||||
OK active lock refuses a concurrent sync
|
||||
OK source symlink fails closed
|
||||
OK nested second entrypoint fails closed
|
||||
|
||||
extension package selftest: 18 passed, 0 failed
|
||||
@@ -0,0 +1,53 @@
|
||||
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
|
||||
|
||||
OK syntax: scripts/foundation-inspect.mjs
|
||||
OK syntax: scripts/foundation/strict-json.mjs
|
||||
OK syntax: scripts/foundation/canonical.mjs
|
||||
OK syntax: scripts/foundation/resolve.mjs
|
||||
OK syntax: scripts/foundation/validate-record.mjs
|
||||
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
|
||||
OK syntax: scripts/foundation/canonical.test.mjs
|
||||
OK syntax: scripts/foundation/cli.test.mjs
|
||||
OK syntax: scripts/foundation/fixtures.test.mjs
|
||||
OK syntax: scripts/foundation/resolve.test.mjs
|
||||
OK syntax: scripts/foundation/strict-json.test.mjs
|
||||
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
|
||||
OK fixture generator runs
|
||||
OK checked-in fixtures/bundles equal a fresh generation
|
||||
OK checked-in fixtures/raw equal a fresh generation
|
||||
OK checked-in fixtures/index.json equal a fresh generation
|
||||
OK checked-in demo bundles equal a fresh generation
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test scripts/foundation/ (ℹ pass 80)
|
||||
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
|
||||
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
|
||||
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
|
||||
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
|
||||
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
|
||||
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
|
||||
OK oracle: zero schema-column disagreements with the pinned checker
|
||||
OK oracle: strict-only profile refusals are counted and asserted
|
||||
OK demo: permitted read preview exits 0 (exit 0)
|
||||
OK demo: permitted file.change preview exits 0 (exit 0)
|
||||
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
|
||||
OK demo: revoked registration is refused (exit 3) (exit 3)
|
||||
OK demo: message is not authority (exit 3) (exit 3)
|
||||
OK usage: no arguments exits 2 (exit 2)
|
||||
OK io: missing file exits 4 (exit 4)
|
||||
OK io: directory exits 4 (exit 4)
|
||||
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
|
||||
OK bound: oversize fixture exits 2 (exit 2)
|
||||
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
|
||||
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
|
||||
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
|
||||
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
|
||||
OK text output starts with the disclaimer
|
||||
OK json output is valid JSON with result allowed and exactly the charter §7 fields
|
||||
OK json golden matches byte-for-byte
|
||||
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
|
||||
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
|
||||
OK canary never printed (bundle run and credential-file run)
|
||||
OK a non-bundle JSON file is refused at the shape gate, not read into output
|
||||
OK no field of the non-bundle file is echoed
|
||||
|
||||
selftest: 44 passed, 0 failed
|
||||
@@ -0,0 +1,35 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 149
|
||||
ℹ pass 149
|
||||
ℹ fail 0
|
||||
OK node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/darkwing-scratch/r54a/papply) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
@@ -0,0 +1,17 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
OK status safe on empty state (exit 0)
|
||||
OK status created no pointer
|
||||
OK fault-injected activation refuses (exit 1)
|
||||
OK refused activation wrote no pointer
|
||||
OK refusal logged exactly once with valid fields
|
||||
OK healthy activation succeeds (exit 0)
|
||||
OK pointer written with valid fields
|
||||
OK repeat activation succeeds (log grows) (exit 0)
|
||||
OK log is append-only across activations
|
||||
OK rollback without previous refuses (exit 1)
|
||||
|
||||
selftest: 14 passed, 0 failed
|
||||
exit=0
|
||||
@@ -0,0 +1,7 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
skip state-machine cases (docker daemon unavailable)
|
||||
|
||||
selftest: 4 passed, 0 failed
|
||||
@@ -0,0 +1,32 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
skip adapter seam cases (docker daemon unavailable)
|
||||
skip workspace/capability cases (docker daemon unavailable)
|
||||
skip live task cases (docker unavailable)
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
skip live recall pair: user recall run, recalled user name (docker daemon unavailable)
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 26 passed, 0 failed
|
||||
@@ -0,0 +1,28 @@
|
||||
fixture arbiters: {"delivery":"pm","technical":"cto"}
|
||||
B1 api arbiters: {"delivery":"pm","technical":"cto"}
|
||||
B1 Arbiters dd: "none"
|
||||
B2 mirrors: ["palette","mode"]
|
||||
B2 palette before: ["SELECT","palette"]
|
||||
B2 palette due refreshes: 1
|
||||
B2 palette fired: 1
|
||||
B2 palette after: ["BODY",""]
|
||||
B2 mode before: ["SELECT","mode"]
|
||||
B2 mode due refreshes: 1
|
||||
B2 mode fired: 1
|
||||
B2 mode after: ["BODY",""]
|
||||
B2b before: ["harbor",["harbor","carmine","atlantic","terracotta","aubergine","mineral","cobalt","rosewood","graphite","grove"]]
|
||||
B2b after: ["carmine","carmine"]
|
||||
B3 before: "H1"
|
||||
B3 fired: 1
|
||||
B3 after: ["H1","Queue"]
|
||||
B4 due on queue: 1
|
||||
B4 due after nav: 1
|
||||
✔ probes, seeded fixture (2076.585529ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2167.431061
|
||||
@@ -0,0 +1,73 @@
|
||||
// Darkwing row 54 round 1 browser probes on the seeded fixture with the real reads.
|
||||
// B1: the Business view's Arbiters line. B2: focus on a Settings mirror select across the 10 s refresh.
|
||||
// B3: focus on the H1 across the 10 s refresh. B4: navigation clears the due refresh.
|
||||
// Run from a worktree's packages/webui: node --test <this file> with WT set to that worktree.
|
||||
import { test, after } from 'node:test';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
const WT = process.env.WT;
|
||||
const u = p => pathToFileURL(`${WT}/packages/webui/${p}`).href;
|
||||
const { browser } = await import(u('tests/browser.mjs'));
|
||||
const { close } = await import(u('tests/fixture.mjs'));
|
||||
const { startServer } = await import(u('src/serve.mjs'));
|
||||
const { consoleReads } = await import(u('src/reads.mjs'));
|
||||
const { REPO_ROLES } = await import(pathToFileURL(`${WT}/packages/business/tests/helpers.mjs`).href);
|
||||
const { seeded, queueRepo } = await import(u('tests/reads-fixture.mjs'));
|
||||
const out = [];
|
||||
const note = (k, v) => { out.push(`${k}: ${JSON.stringify(v)}`); };
|
||||
after(() => console.log(out.join('\n')));
|
||||
|
||||
const PROBE = '(()=>{const st=setTimeout,ct=clearTimeout;window.refreshes=new Map();window.setTimeout=(fn,ms,...a)=>{if(ms!==10000||!String(fn).includes("render(false)"))return st(fn,ms,...a);const id=st(()=>{refreshes.delete(id);fn()},ms);refreshes.set(id,fn);return id};window.clearTimeout=id=>{refreshes.delete(id);return ct(id)};window.fireRefresh=()=>{const due=[...refreshes];refreshes.clear();for(const[id,fn]of due){ct(id);fn()}return due.length}})();';
|
||||
|
||||
test('probes, seeded fixture', { timeout: 120000 }, async t => {
|
||||
const s = seeded(t), repo = queueRepo(t);
|
||||
note('fixture arbiters', s.doc.arbiters);
|
||||
const web = await startServer({ port: 0, board: 'http://127.0.0.1:9', reads: consoleReads({ root: repo.root, env: repo.env, system: s.system, configDir: s.configDir, rolesDir: REPO_ROLES, business: 'acme' }) });
|
||||
const b = await browser();
|
||||
try {
|
||||
const wait = expr => b.evaluate(`(async()=>{for(let i=0;i<200;i++){if(${expr})return true;await new Promise(r=>setTimeout(r,50))}throw new Error('Condition timed out: '+${JSON.stringify(expr)})})()`);
|
||||
const go = async (hash, h1) => { await b.evaluate(`location.hash=${JSON.stringify(hash)}`); await wait(`document.querySelector("#bus-view h1")?.textContent.startsWith(${JSON.stringify(h1)}) && !document.querySelector("#bus-view [aria-busy]")`); };
|
||||
await b.call('Page.addScriptToEvaluateOnNewDocument', { source: PROBE });
|
||||
await b.viewport(1200, 900);
|
||||
await b.navigate(`http://127.0.0.1:${web.address().port}/`);
|
||||
await wait('document.querySelectorAll("#sections a").length===7');
|
||||
|
||||
// B1
|
||||
await go('#/business', 'Business acme');
|
||||
note('B1 api arbiters', await b.evaluate('fetch("/api/business").then(r=>r.json()).then(j=>j.business.arbiters)'));
|
||||
note('B1 Arbiters dd', await b.evaluate('(()=>{const dt=[...document.querySelectorAll("#bus-view dt")].find(e=>e.textContent==="Arbiters");return dt?.nextElementSibling?.textContent})()'));
|
||||
|
||||
// B2
|
||||
await go('#/settings', 'Settings');
|
||||
const sels = await b.evaluate('[...document.querySelectorAll("#bus-view select[data-mirror]")].map(e=>e.dataset.mirror)');
|
||||
note('B2 mirrors', sels);
|
||||
for (const m of sels) {
|
||||
await b.evaluate(`document.querySelector('#bus-view select[data-mirror="${m}"]').focus()`);
|
||||
note(`B2 ${m} before`, await b.evaluate('[document.activeElement.tagName, document.activeElement.dataset?.mirror]'));
|
||||
note(`B2 ${m} due refreshes`, await b.evaluate('window.refreshes.size'));
|
||||
note(`B2 ${m} fired`, await b.evaluate('window.fireRefresh()'));
|
||||
await wait('!document.querySelector("#bus-view [aria-busy]")');
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
note(`B2 ${m} after`, await b.evaluate('[document.activeElement.tagName, document.activeElement.dataset?.mirror ?? document.activeElement.id]'));
|
||||
}
|
||||
// B2b: change a mirror and see the command bar select follow
|
||||
const m0 = sels[0];
|
||||
note('B2b before', await b.evaluate(`[document.getElementById("${m0}").value, [...document.getElementById("${m0}").options].map(o=>o.value)]`));
|
||||
await b.evaluate(`(()=>{const s=document.querySelector('#bus-view select[data-mirror="${m0}"]');const v=[...s.options].map(o=>o.value).find(v=>v!==s.value);s.value=v;s.dispatchEvent(new Event('change',{bubbles:true}));})()`);
|
||||
note('B2b after', await b.evaluate(`[document.getElementById("${m0}").value, document.querySelector('#bus-view select[data-mirror="${m0}"]').value]`));
|
||||
|
||||
// B3
|
||||
await go('#/queue', 'Queue');
|
||||
await b.evaluate('(()=>{const h=document.querySelector("#bus-view h1");h.tabIndex=-1;h.focus()})()');
|
||||
note('B3 before', await b.evaluate('document.activeElement.tagName'));
|
||||
note('B3 fired', await b.evaluate('window.fireRefresh()'));
|
||||
await wait('!document.querySelector("#bus-view [aria-busy]")');
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
note('B3 after', await b.evaluate('[document.activeElement.tagName, document.activeElement.textContent.slice(0,20)]'));
|
||||
|
||||
// B4
|
||||
await go('#/queue', 'Queue');
|
||||
note('B4 due on queue', await b.evaluate('window.refreshes.size'));
|
||||
await go('#/business', 'Business acme');
|
||||
note('B4 due after nav', await b.evaluate('window.refreshes.size'));
|
||||
} finally { await b.close(); await close(web); }
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
fixture arbiters: {"delivery":"pm","technical":"cto"}
|
||||
B1 api arbiters: {"delivery":"pm","technical":"cto"}
|
||||
B1 Arbiters dd: "none"
|
||||
B2 mirrors: ["palette","mode"]
|
||||
B2 palette before: ["SELECT","palette"]
|
||||
B2 palette due refreshes: 1
|
||||
B2 palette fired: 1
|
||||
B2 palette after: ["SELECT","palette"]
|
||||
B2 mode before: ["SELECT","mode"]
|
||||
B2 mode due refreshes: 1
|
||||
B2 mode fired: 1
|
||||
B2 mode after: ["SELECT","mode"]
|
||||
B2b before: ["harbor",["harbor","carmine","atlantic","terracotta","aubergine","mineral","cobalt","rosewood","graphite","grove"]]
|
||||
B2b after: ["carmine","carmine"]
|
||||
B3 before: "H1"
|
||||
B3 fired: 1
|
||||
B3 after: ["H1","Queue"]
|
||||
B4 due on queue: 1
|
||||
B4 due after nav: 1
|
||||
✔ probes, seeded fixture (2002.571259ms)
|
||||
ℹ tests 1
|
||||
ℹ suites 0
|
||||
ℹ pass 1
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2077.350586
|
||||
@@ -0,0 +1,18 @@
|
||||
// P4f: the real queue-read.mjs when its store import fails at load (a broken
|
||||
// or partial checkout). What reaches the read's message?
|
||||
import { mkdtempSync, writeFileSync, mkdirSync, realpathSync, rmSync, copyFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
const WT = process.env.WT;
|
||||
const { consoleReads } = await import(`${WT}/packages/webui/src/reads.mjs`);
|
||||
const base = realpathSync(mkdtempSync(join(tmpdir(), 'dw-r54-imp-')));
|
||||
for (const [name, store] of [['syntax', 'export function rows( {'], ['missing', null]]) {
|
||||
const root = join(base, name);
|
||||
mkdirSync(join(root, 'packages/webui/src'), { recursive: true }); mkdirSync(join(root, 'packages/queue/src'), { recursive: true });
|
||||
copyFileSync(join(WT, 'packages/webui/src/queue-read.mjs'), join(root, 'packages/webui/src/queue-read.mjs'));
|
||||
writeFileSync(join(root, 'packages/queue/src/errors.mjs'), 'export class QueueError extends Error {}');
|
||||
if (store) writeFileSync(join(root, 'packages/queue/src/store.mjs'), store);
|
||||
try { await consoleReads({ root }).queue(); console.log(`${name}: resolved`); }
|
||||
catch (e) { console.log(`${name}: ${e.code}\n${e.message.replaceAll(base, '<BASE>')}\nleaks base: ${e.message.includes(base)}\n`); }
|
||||
}
|
||||
rmSync(base, { recursive: true, force: true });
|
||||
@@ -0,0 +1,43 @@
|
||||
syntax: read-failed
|
||||
file://<BASE>/syntax/packages/queue/src/store.mjs:1
|
||||
export function rows( {
|
||||
|
||||
|
||||
SyntaxError: Unexpected end of input
|
||||
at compileSourceTextModule (node:internal/modules/esm/utils:355:16)
|
||||
at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:93:18)
|
||||
at #translate (node:internal/modules/esm/loader:441:20)
|
||||
at afterLoad (node:internal/modules/esm/loader:509:29)
|
||||
at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:514:12)
|
||||
at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:567:36)
|
||||
at afterResolve (node:internal/modules/esm/loader:614:52)
|
||||
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:620:12)
|
||||
at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)
|
||||
at ModuleJob.link (node:internal/modules/esm/module_job:389:17)
|
||||
|
||||
Node.js v26.8.1
|
||||
leaks base: true
|
||||
|
||||
missing: read-failed
|
||||
node:internal/modules/esm/resolve:272
|
||||
throw new ERR_MODULE_NOT_FOUND(
|
||||
^
|
||||
|
||||
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '<path>' imported from <path>
|
||||
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
|
||||
at moduleResolve (node:internal/modules/esm/resolve:879:10)
|
||||
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
|
||||
at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)
|
||||
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)
|
||||
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:766:56)
|
||||
at #resolve (node:internal/modules/esm/loader:690:17)
|
||||
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:610:35)
|
||||
at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)
|
||||
at ModuleJob.link (node:internal/modules/esm/module_job:389:17) {
|
||||
code: 'ERR_MODULE_NOT_FOUND',
|
||||
url: 'file://<BASE>/missing/packages/queue/src/store.mjs'
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
leaks base: true
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// Row 54 round 1 node probes (Darkwing). Run with WT=<candidate worktree>.
|
||||
import { mkdtempSync, writeFileSync, mkdirSync, realpathSync, rmSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
const WT = process.env.WT;
|
||||
const { redactor, queueReader, consoleReads, businessId } = await import(`${WT}/packages/webui/src/reads.mjs`);
|
||||
const { startServer } = await import(`${WT}/packages/webui/src/serve.mjs`);
|
||||
const { startTimeOf, hostFile } = await import(`${WT}/packages/cli/src/host.mjs`);
|
||||
const out = (k, v) => console.log(`${k}: ${JSON.stringify(v)}`);
|
||||
|
||||
// P2: the redactor on shapes its tests don't use.
|
||||
const r = redactor({ configDir: '/home/u/.config/mosaic-dev', dataRoot: '/home/u/.mosaic-dev' });
|
||||
for (const s of [
|
||||
'config:/home/u/secret/x.token', 'open "/home/u/secret/x.token"', 'path</home/u/x>', 'see file:///home/u/x.token', 'at ~/secret/x.token',
|
||||
'in ./secret/x.token', 'C:\\Users\\u\\x.token', 'url https://discord.com/channels/123456789012345678/223456789012345678',
|
||||
'id:123456789012345678', 'id_123456789012345678', 'n=1234567890123456', 'a 21-digit 123456789012345678901 run',
|
||||
'tab\t/home/u/x', '/home/u/.config/mosaic-dev', '/home/u/.config/mosaic-dev/', '/home/u/.mosaic-dev.bak/x', '[/home/u/x]', '{/home/u/x}',
|
||||
]) out(`P2 ${JSON.stringify(s)}`, r(s));
|
||||
|
||||
// P4: the queue child's timeout and output cap, with a stand-in reader script.
|
||||
const base = realpathSync(mkdtempSync(join(tmpdir(), 'dw-r54-')));
|
||||
const fake = (name, body) => { const root = join(base, name); mkdirSync(join(root, 'packages/webui/src'), { recursive: true }); writeFileSync(join(root, 'packages/webui/src/queue-read.mjs'), body); return root; };
|
||||
const settle = async (p) => { const t = Date.now(); try { const v = await p; return { ok: true, ms: Date.now() - t, rows: v.rows?.length }; } catch (e) { return { ok: false, ms: Date.now() - t, code: e.code, message: e.message }; } };
|
||||
out('P4a hang, timeout 500ms', await settle(queueReader({ root: fake('hang', 'setInterval(()=>{},1000)'), timeoutMs: 500 })()));
|
||||
out('P4b 64 KiB out, cap 1 KiB', await settle(queueReader({ root: fake('big', 'process.stdout.write("x".repeat(65536))'), maxBytes: 1024 })()));
|
||||
out('P4c exit 2 with a path on stderr', await settle(consoleReads({ root: fake('two', `process.stderr.write("refused at ${base}/q.json");process.exit(2)`) }).queue()));
|
||||
out('P4d exit 0, bad shape', await settle(queueReader({ root: fake('shape', 'console.log(JSON.stringify({rows:{}}))') })()));
|
||||
out('P4e killed by a signal', await settle(queueReader({ root: fake('sig', 'process.kill(process.pid,"SIGKILL")') })()));
|
||||
|
||||
// P5: the business id from the bus host state: none, stale, live, malformed.
|
||||
const dataRoot = join(base, 'data'); mkdirSync(join(dataRoot, 'bus-host'), { recursive: true });
|
||||
const id = () => { try { return businessId({ dataRoot }); } catch (e) { return `${e.code}: ${e.message}`; } };
|
||||
out('P5a no host file', id());
|
||||
writeFileSync(hostFile(dataRoot), JSON.stringify({ pid: process.pid, startTime: 'not-the-start', business: 'stale-biz' }));
|
||||
out('P5b stale host file', id());
|
||||
writeFileSync(hostFile(dataRoot), JSON.stringify({ pid: process.pid, startTime: startTimeOf(process.pid), business: 'live-biz' }));
|
||||
out('P5c live host file', id());
|
||||
writeFileSync(hostFile(dataRoot), '{');
|
||||
out('P5d malformed host file', id());
|
||||
|
||||
// P3: Host and Origin on the new routes, and HEAD.
|
||||
const server = await startServer({ port: 0, reads: consoleReads({ root: fake('two2', 'process.exit(2)') }) });
|
||||
const port = server.address().port;
|
||||
const { request } = await import('node:http');
|
||||
const call = (path, method = 'GET', headers = {}) => new Promise(res => { const q = request({ host: '127.0.0.1', port, path, method, headers }, s => { let b = ''; s.on('data', c => b += c); s.on('end', () => res(`${s.statusCode} ${b.slice(0, 80)}`)); }); q.end(); });
|
||||
for (const p of ['/api/queue', '/api/queue/1', '/api/business', '/api/settings']) {
|
||||
out(`P3 ${p} Host evil`, await call(p, 'GET', { host: 'evil.example' }));
|
||||
out(`P3 ${p} Host 127.0.0.1:other`, await call(p, 'GET', { host: '127.0.0.1:1' }));
|
||||
out(`P3 ${p} Origin evil`, await call(p, 'GET', { origin: 'http://evil.example' }));
|
||||
out(`P3 ${p} HEAD`, await call(p, 'HEAD'));
|
||||
out(`P3 ${p} OPTIONS`, await call(p, 'OPTIONS'));
|
||||
}
|
||||
out('P3 /api/queue/ empty id', await call('/api/queue/'));
|
||||
out('P3 /api/queue/1?x', await call('/api/queue/1?x=1'));
|
||||
out('P3 /api/queue%2F1', await call('/api/queue%2F1'));
|
||||
server.close();
|
||||
rmSync(base, { recursive: true, force: true });
|
||||
@@ -0,0 +1,50 @@
|
||||
P2 "config:/home/u/secret/x.token": "config:/home/u/secret/x.token"
|
||||
P2 "open \"/home/u/secret/x.token\"": "open \"<path>\""
|
||||
P2 "path</home/u/x>": "path</home/u/x>"
|
||||
P2 "see file:///home/u/x.token": "see file:///home/u/x.token"
|
||||
P2 "at ~/secret/x.token": "at ~/secret/x.token"
|
||||
P2 "in ./secret/x.token": "in ./secret/x.token"
|
||||
P2 "C:\\Users\\u\\x.token": "C:\\Users\\u\\x.token"
|
||||
P2 "url https://discord.com/channels/123456789012345678/223456789012345678": "url https://discord.com/channels/<id>/<id>"
|
||||
P2 "id:123456789012345678": "id:<id>"
|
||||
P2 "id_123456789012345678": "id_123456789012345678"
|
||||
P2 "n=1234567890123456": "n=1234567890123456"
|
||||
P2 "a 21-digit 123456789012345678901 run": "a 21-digit 123456789012345678901 run"
|
||||
P2 "tab\t/home/u/x": "tab\t<path>"
|
||||
P2 "/home/u/.config/mosaic-dev": "<config>"
|
||||
P2 "/home/u/.config/mosaic-dev/": "<config>/"
|
||||
P2 "/home/u/.mosaic-dev.bak/x": "<path>"
|
||||
P2 "[/home/u/x]": "[<path>]"
|
||||
P2 "{/home/u/x}": "{<path>}"
|
||||
P4a hang, timeout 500ms: {"ok":false,"ms":500,"code":"read-failed","message":"the queue read did not finish in time"}
|
||||
P4b 64 KiB out, cap 1 KiB: {"ok":false,"ms":22,"code":"invalid-response","message":"the queue read printed too much"}
|
||||
P4c exit 2 with a path on stderr: {"ok":false,"ms":26,"code":"queue-refused","message":"refused at <path>"}
|
||||
P4d exit 0, bad shape: {"ok":false,"ms":29,"code":"invalid-response","message":"the queue read printed something that is not its JSON"}
|
||||
P4e killed by a signal: {"ok":false,"ms":22,"code":"read-failed","message":"the queue read did not finish"}
|
||||
P5a no host file: "no-bus-host: no bus host is running and no --business was given"
|
||||
P5b stale host file: "no-bus-host: no bus host is running and no --business was given"
|
||||
P5c live host file: "live-biz"
|
||||
P5d malformed host file: "no-bus-host: the bus host state file is unreadable"
|
||||
P3 /api/queue Host evil: "403 {\"error\":\"non-local Host refused\"}"
|
||||
P3 /api/queue Host 127.0.0.1:other: "403 {\"error\":\"non-local Host refused\"}"
|
||||
P3 /api/queue Origin evil: "403 {\"error\":\"cross-origin request refused\"}"
|
||||
P3 /api/queue HEAD: "405 "
|
||||
P3 /api/queue OPTIONS: "405 {\"error\":\"method not allowed\"}"
|
||||
P3 /api/queue/1 Host evil: "403 {\"error\":\"non-local Host refused\"}"
|
||||
P3 /api/queue/1 Host 127.0.0.1:other: "403 {\"error\":\"non-local Host refused\"}"
|
||||
P3 /api/queue/1 Origin evil: "403 {\"error\":\"cross-origin request refused\"}"
|
||||
P3 /api/queue/1 HEAD: "405 "
|
||||
P3 /api/queue/1 OPTIONS: "405 {\"error\":\"method not allowed\"}"
|
||||
P3 /api/business Host evil: "403 {\"error\":\"non-local Host refused\"}"
|
||||
P3 /api/business Host 127.0.0.1:other: "403 {\"error\":\"non-local Host refused\"}"
|
||||
P3 /api/business Origin evil: "403 {\"error\":\"cross-origin request refused\"}"
|
||||
P3 /api/business HEAD: "405 "
|
||||
P3 /api/business OPTIONS: "405 {\"error\":\"method not allowed\"}"
|
||||
P3 /api/settings Host evil: "403 {\"error\":\"non-local Host refused\"}"
|
||||
P3 /api/settings Host 127.0.0.1:other: "403 {\"error\":\"non-local Host refused\"}"
|
||||
P3 /api/settings Origin evil: "403 {\"error\":\"cross-origin request refused\"}"
|
||||
P3 /api/settings HEAD: "405 "
|
||||
P3 /api/settings OPTIONS: "405 {\"error\":\"method not allowed\"}"
|
||||
P3 /api/queue/ empty id: "400 {\"error\":\"invalid-request\",\"message\":\"a queue row id is a positive integer\"}"
|
||||
P3 /api/queue/1?x: "503 {\"error\":\"queue-refused\",\"message\":\"the queue refused the read\"}"
|
||||
P3 /api/queue%2F1: "404 {\"error\":\"not found\"}"
|
||||
@@ -0,0 +1,240 @@
|
||||
# Row 54, read-only console views, round 1 review (Darkwing)
|
||||
|
||||
Issue #1543, request comment 27182, packet comment 27183. Base
|
||||
`d64f434f`. Candidate manifest sha256
|
||||
`dba2429e411ae22b1e939344247e9f3857a48e488dc7da11c578d1092288ea8a`, 14
|
||||
files. The packet manifest and my snapshot of the checkout both check 14
|
||||
OK, and `row54.patch` applied at `c6b214e5` gives the same 14 files.
|
||||
`c6b214e5` is `d64f434f` plus queue revs 373 to 375, which touch only
|
||||
`docs/plans/queue.json` and `QUEUE.md`.
|
||||
|
||||
Verdict: **changes**. The four fixes Sage asked me to check are right, and
|
||||
the tests pin each one. Every suite is green. Two things block. The
|
||||
Business view shows "none" for Arbiters on every real business file, and a
|
||||
queue child that fails at import sends an absolute `file://` path and a
|
||||
stack trace to the browser.
|
||||
|
||||
## Method
|
||||
|
||||
- Detached worktrees `papply` and `mutwt` at `c6b214e5` with
|
||||
`row54.patch` applied and staged. Both check 14 OK.
|
||||
`mutwt` still checks 14 OK after the mutant runs
|
||||
(`r1/mut/manifest-after.txt`).
|
||||
- `r1/gate.sh`: the `packages/webui`, `packages/queue` and
|
||||
`packages/business` node suites, `build-tokens.mjs --check`, then every
|
||||
`scripts/test-*.sh` with `DOCKER_HOST=unix:///nonexistent.sock`,
|
||||
20:05:27Z to 20:08:51Z. I skipped the `packages/cli` node suite:
|
||||
`packages/cli` doesn't change and is frozen.
|
||||
- 31 mutants (`r1/mut/mutate.py`, `r1/mut/run.sh`), each against the full
|
||||
`packages/webui` suite, except D31 against `packages/queue`, 20:09:04Z
|
||||
to 20:37:00Z.
|
||||
- `test-release` with Docker.
|
||||
- Node probes (`r1/probes/probe-node.mjs`, output `probe-node.txt`): the
|
||||
redactor on hostile strings (P2), the four routes under bad Host, bad
|
||||
Origin, HEAD, OPTIONS and odd ids (P3), the queue child against a fake
|
||||
script that hangs, floods, refuses, prints the wrong shape or dies (P4),
|
||||
and the bus host state file (P5).
|
||||
- An import probe (`r1/probes/probe-import.mjs`, output
|
||||
`probe-import.txt`): the real `queue-read.mjs` beside a `store.mjs` with
|
||||
a syntax error, and beside none (P4f).
|
||||
- Browser probes in Chromium through the candidate's `tests/browser.mjs`
|
||||
on the seeded fixture with the real reads
|
||||
(`r1/probes/probe-browser.test.mjs`, output `probe-browser.txt`): B1 to
|
||||
B4.
|
||||
- Read every changed file against the brief, decision 83 and my row 53
|
||||
notes.
|
||||
|
||||
Node v26.8.1, `TMPDIR=~/darkwing-scratch/r54a/tmp`. No tracker request.
|
||||
|
||||
## Suites
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| packages/webui (node) | 38/0 |
|
||||
| packages/queue (node) | 149/0 |
|
||||
| packages/business (node) | 60/0 |
|
||||
| build-tokens --check | rc 0, "tokens.css is current" |
|
||||
| test-auth | 15/0 |
|
||||
| test-conductor | 17/0 |
|
||||
| test-config | 24/0 |
|
||||
| test-discord | 66/0 |
|
||||
| test-extension-package | 18/0 |
|
||||
| test-foundation | 44/0 |
|
||||
| test-queue | 27/0 |
|
||||
| test-release | 4/0 without Docker, 14/0 with it |
|
||||
| test-task, Docker unreachable | 26/0 |
|
||||
|
||||
I didn't run test-task with real Docker, because it makes live model calls.
|
||||
|
||||
## The four fixes
|
||||
|
||||
| Item | Result |
|
||||
|---|---|
|
||||
| Refresh timer during a navigation | Fixed. `clearTimeout(timer)` is the first line of `render()`. D01 drops it and dies at "the navigation cleared the due refresh" (`views.test.mjs:150`) |
|
||||
| H1 focus on a same-page refresh | Fixed. B3: focus on the Queue H1, fire the 10 s refresh, focus is still on the new H1. D02 and D03 die (table below) |
|
||||
| Stale `#conv-pick` | Fixed. `openConversation()` empties it beside `#conv-meta`. D04 dies at "no stale session in the picker" |
|
||||
| Focus after `error()` closes a conversation | Fixed, with Dewey's check on prior focus. D05 and D06 both die |
|
||||
|
||||
On the `error()` deviation: I agree with Dewey. My note asked for the
|
||||
fallback without the check, and that would pull focus to `<main>` and
|
||||
scroll on every failed ten-second refresh when nothing had focus. The
|
||||
check is better than what I asked for.
|
||||
|
||||
B2 also covers the mirror selects, which `keep()` and `restore()` now
|
||||
carry: focus on each Settings mirror (`palette`, `mode`) survives the 10 s
|
||||
refresh, and a change on the mirror moves the command bar's select.
|
||||
|
||||
## Required
|
||||
|
||||
1. **Arbiters always reads "none".** `reads.mjs:149` passes
|
||||
`b.arbiters` through as the business file has it, an object
|
||||
(`{ delivery, technical }`, which `packages/bus` requires). `bus.js:404`
|
||||
renders it with `names()`, and `names()` (`bus.js:348`) returns `none`
|
||||
for anything but a non-empty array. B1 on the seeded fixture: the API
|
||||
answers `{"delivery":"pm","technical":"cto"}` and the page shows "none".
|
||||
The views.test stub has `arbiters: { delivery: 'pm' }` and nothing
|
||||
asserts the Arbiters line, so no test sees it. Render the object as
|
||||
pairs (for example "pm (delivery), cto (technical)"), and assert the
|
||||
line on the seeded fixture, not only on the stub.
|
||||
2. **An import failure in the queue child sends an absolute path to the
|
||||
browser.** `queue-read.mjs` imports `store.mjs` and `errors.mjs`
|
||||
statically, so a failure at load time happens before its `try`. Node
|
||||
prints the error and a stack to stderr and exits 1. `reads.mjs:81`
|
||||
passes up to 2000 characters of that stderr as the `read-failed`
|
||||
message, and the redactor doesn't match a path after `file://`
|
||||
(the character before the `/` is `:`, which isn't in its prefix set).
|
||||
P4f, with the real `queue-read.mjs`:
|
||||
- `store.mjs` with a syntax error: the message starts
|
||||
`file://<BASE>/syntax/packages/queue/src/store.mjs:1`, then the
|
||||
broken source line, then the stack.
|
||||
- no `store.mjs`: the message ends with
|
||||
`url: 'file://<BASE>/missing/packages/queue/src/store.mjs'`.
|
||||
|
||||
`<BASE>` is my substitution in the probe output; the real message
|
||||
carries the absolute temporary path. This checkout is edited in place
|
||||
by several seats, so a half-written `store.mjs` is a real state, not a
|
||||
contrived one. The brief says no file path appears in any response.
|
||||
Any one of these closes it, and I'd do the first two:
|
||||
- import the store inside the `try` (a dynamic `import()`), so a load
|
||||
failure prints "the queue read failed";
|
||||
- forward stderr only for exit 2, with a fixed message for any other
|
||||
exit;
|
||||
- add `:` (or `file:`) to the redactor's prefix set.
|
||||
|
||||
A test with a broken `store.mjs` in the `queueRepo` scratch copy should
|
||||
assert that the message carries no base path.
|
||||
|
||||
## Notes (not blocking)
|
||||
|
||||
1. Redactor gaps from P2. These pass through unchanged:
|
||||
`config:/home/u/x.token`, `path</home/u/x>`, `~/secret/x.token`,
|
||||
`./secret/x.token`, a Windows path, `id_123456789012345678`, and
|
||||
digit runs of 16 or 21. None of the sources I traced produces the
|
||||
first two today. `~/` paths do show up in real queue notes (`~/.mosaic`
|
||||
in three), and the Queue row page shows them as written. They name no
|
||||
user and no secret, so I'd leave them. The README's "any other absolute
|
||||
path" describes this correctly.
|
||||
2. Five mutants survive (table below). D14 is equivalent. D20 to D22 are
|
||||
paths my probes show working with no test. D29 is a test gap for
|
||||
mirror focus across a refresh. I'd add the D29 test with the round 2
|
||||
fixes; the others can wait.
|
||||
3. `cli.mjs`'s outer catch prints its error unredacted. Dewey lists it
|
||||
under "Not done"; I agree its inputs carry no path today.
|
||||
4. `bus.js:428` lost a space: `const GROUPS =[[`. Cosmetic.
|
||||
5. B4 is weaker than Dewey's test: after a navigation one refresh is due,
|
||||
and that is the new view's timer. Dewey's PROBE checks the count with
|
||||
the reads held, which is the right shape. D01 confirms it.
|
||||
6. views.test test 1 failed at line 167 (the palette's queue rows) in two
|
||||
mutant runs that can't reach it, and passed in eight clean runs. Worth
|
||||
a look before it fails someone's gate. Two guesses, neither checked: the
|
||||
Ctrl+K press lands before the palette's handler sees the last queue
|
||||
read, or a real 10 s refresh fires during the test (it runs about 5 s,
|
||||
but slower under load).
|
||||
|
||||
## Probes
|
||||
|
||||
| Probe | Candidate |
|
||||
|---|---|
|
||||
| P2 redactor | quoted, bracketed, braced and tab-led paths become `<path>`; the config dir is `<config>`; `.mosaic-dev.bak` is `<path>`; Discord URL ids and `id:` ids are `<id>`. Gaps in note 1 |
|
||||
| P3 routes | on all four: bad Host 403, bad Origin 403, HEAD and OPTIONS 405. `/api/queue/` 400, `/api/queue%2F1` 404, `/api/queue/1?x=1` reaches the read |
|
||||
| P4a child hangs, 500 ms timeout | `read-failed` "did not finish in time" at 500 ms |
|
||||
| P4b 64 KiB out, 1 KiB cap | `invalid-response` "printed too much" |
|
||||
| P4c exit 2 with a path | `queue-refused` "refused at `<path>`" |
|
||||
| P4d wrong shape | `invalid-response` |
|
||||
| P4e killed by a signal | `read-failed` "did not finish" |
|
||||
| **P4f** import failure | absolute `file://` path and stack in the message. Required 2 |
|
||||
| P5 bus host state | no file and a stale file give `no-bus-host`; a live file gives its business; a malformed file gives `no-bus-host` "unreadable" |
|
||||
| **B1** Business, seeded | API `{"delivery":"pm","technical":"cto"}`, Arbiters "none". Required 1 |
|
||||
| B2 mirror focus across the refresh | kept on `palette` and on `mode`; a mirror change moves the command bar |
|
||||
| B3 H1 focus across the refresh | kept |
|
||||
| B4 navigation | one refresh due after it. See note 5 |
|
||||
|
||||
## Mutants
|
||||
|
||||
26 of 31 killed (`r1/mut/summary.txt`). I checked each kill site in its
|
||||
output against its diff.
|
||||
|
||||
| Mutant | Change | Result |
|
||||
|---|---|---|
|
||||
| D01 | no `clearTimeout(timer)` at the top of `render()` | killed, "the navigation cleared the due refresh", `views.test.mjs:150` |
|
||||
| D02 | `keep()` doesn't record an H1 | killed, "#/queue keeps heading focus", `views.test.mjs:95` |
|
||||
| D03 | `restore()` doesn't refocus the H1 | killed, same line |
|
||||
| D04 | no `#conv-pick` clear | killed, "no stale session in the picker", `shell.test.mjs:220` |
|
||||
| D05 | no focus fallback in `error()` | killed, "focus after a refusal closes the conversation", `shell.test.mjs:231` |
|
||||
| D06 | the fallback without `focused &&` | killed, "a failure with nothing focused leaves focus alone", `shell.test.mjs:191` |
|
||||
| D07 | no generic path rule in the redactor | killed, redactor test `reads.test.mjs:23` and "response carries /srv/secret" |
|
||||
| D08 | no Discord id rule | killed, `reads.test.mjs:25` and "response carries id 523456789012345678" |
|
||||
| D09 | no unquote | killed, `reads.test.mjs:27` |
|
||||
| D10 | no config and dataRoot labels | killed, `reads.test.mjs:21`, `:110` and `:155` |
|
||||
| D11 | queue list not scrubbed | killed, `reads.test.mjs:49` and "response carries id" in views.test |
|
||||
| D12 | queue row not scrubbed | killed, the row note text, `views.test.mjs:194` |
|
||||
| D13 | business vars not allowlisted | killed, "response carries discordUserId" |
|
||||
| D14 | role vars not allowlisted | **survived**, equivalent today. See below |
|
||||
| D15 | credential spreads its ref | killed, "response carries CODER_GITEA_SECRET_ENV" |
|
||||
| D16 | `fail()` doesn't redact | killed, `reads.test.mjs:110` and `views.test.mjs:208` |
|
||||
| D17 | settings not scrubbed | killed, `reads.test.mjs:155` |
|
||||
| D18 | cli bus line not redacted | killed, "the log names the temporary directory", `views.test.mjs:232` |
|
||||
| D19 | refusal on exit 3, not 2 | killed, `read-failed` for `queue-refused`, `reads.test.mjs:72` |
|
||||
| D20 | no output cap | **survived** |
|
||||
| D21 | timeout times 1000 | **survived** |
|
||||
| D22 | host state without `.live` | **survived** |
|
||||
| D23 | no 405 on the new routes | killed, `reads.test.mjs:61` |
|
||||
| D24 | any digits as a row id | killed, 404 for 400, `reads.test.mjs:59` |
|
||||
| D25 | no 503 without reads | killed, `reads.test.mjs:136` |
|
||||
| D26 | `queue-refused` not a refusal | killed, no refusal banner, `views.test.mjs:107` |
|
||||
| D27 | settings refusal without appearance | killed, 0 mirror selects for 2, `views.test.mjs:114` |
|
||||
| D28 | palette without queue rows | killed, `views.test.mjs:167` |
|
||||
| D29 | `keep()` without the mirror | **survived**. See below |
|
||||
| D30 | mirror change ignored | killed, `#mode` never "dark", `views.test.mjs:177` |
|
||||
| D31 | `rows()` drops its notes | killed, `write.test.mjs:349` |
|
||||
|
||||
D14 is equivalent. The business var registry (`packages/business/src/vars.mjs`)
|
||||
allows only `harness`, `model`, `thinking` and the three `limits.*` keys at
|
||||
the agent layer, which is the allowlist. It stays as defense in depth.
|
||||
|
||||
D20, D21 and D22 are untested paths that work. P4a and P4b show the
|
||||
timeout and the cap behave, and P5c shows a live host file is read. A
|
||||
test for each would be short, with the reader pointed at a fake script.
|
||||
|
||||
D29 is a real gap. With the mutant applied, B2 shows focus on either
|
||||
mirror select falls to `<body>` on the 10 s refresh
|
||||
(`r1/probes/probe-browser-D29.txt`). B2 passes on the candidate, so the
|
||||
code is right and only the test is missing.
|
||||
|
||||
One more thing showed up in the mutant outputs. views.test test 1 timed
|
||||
out at line 167, where Ctrl+K should list `#/queue/7`, under D06, D07 and
|
||||
D09 as well as D28. D07 and D09 change only the redactor, and test 1's
|
||||
stub reads never call it, so those two failures aren't the mutants'
|
||||
doing. D06, D07 and D09 each die at their own assertions as well. Eight clean
|
||||
runs of the webui suite on the candidate passed (`r1/flake/summary.txt`),
|
||||
so it's intermittent and I don't know the cause. See note 6.
|
||||
|
||||
## Files
|
||||
|
||||
- `r1/candidate-manifest.sha256`: copy of Dewey's.
|
||||
- `r1/gate.sh`, `r1/out/`: suite runs, `summary.txt` and
|
||||
`test-release-docker.txt`.
|
||||
- `r1/mut/`: mutant definitions, runner, diffs, outputs, `summary.txt` and
|
||||
the manifest check after the runs.
|
||||
- `r1/probes/`: the three probes and their output, plus B2 under D29.
|
||||
- `r1/flake/`: eight clean runs of the webui suite.
|
||||
Reference in New Issue
Block a user