docs(review): row 40 slice 1 S5 round 2 review, changes (darkwing)
Candidate 5cbf22c0, comment 27001 on #1522. T1 blocks: the terminal's input hold is drained by an earlier chunk's run, so text typed after Ctrl-G then Ctrl-T is refused and cleared. Suites, probes and 16 mutants (13 killed; the 3 survivors checked by hand) in the packet. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
cef660ecd9389500cfcd1e4d16ca7e14a204a158526d747d75ccd6af6b15e79e agents/dewey/work/wui/S5-GATE-E.md
|
||||
416a6014762b06fb6dd463bc90a4f28316dd64e8743ac50aff8b1000c551d5ec agents/dewey/work/wui/SLICE1-VIEWS.md
|
||||
1179401b3252a7e8f54e5bf2f0d7c460d83596cf7140c79cbb38258ec84a7193 agents/dewey/work/wui/TASKS.md
|
||||
3f4c129e7f90fc0840bea48fc9fee44f9eeca212594a26d78bb9fd3aa72933eb agents/dewey/work/wui/evidence/s5-mutants.md
|
||||
035038082edb22de6a63c64c2dd5ae9ec7372ffb1894ebf270a4de005b06800b docs/plans/DEFERRED.md
|
||||
cba8267fd7ab6bb7ce2e4259fcc4f5203d77b66821d0d73e962e7a1c60956149 packages/conversation/README.md
|
||||
38621d505f6ffd859e37c4309f7a2d3a3ee21a53d1ba41b8ff9ca29250a6dc37 packages/conversation/src/cohort.mjs
|
||||
26f8fa2c7caf794e6224802b8e5d8882419d0bda725d2ab30f0a058a678a57b9 packages/conversation/src/controller.mjs
|
||||
a06468434e7bf3a3dfc3c5ea05b8c21a1510f8fa0c8eaa6e9908b970fa98367e packages/conversation/src/pi-pin.mjs
|
||||
72fd7af1475218abcbde15e1801260d6c0db5d3f8c47f09b7aa68e2e47414e0a packages/conversation/src/terminal.mjs
|
||||
a555d3c9394a911cc1e33191eb9112f0a3f0aca8c78ce6adc888a2d40d4a2ffb packages/conversation/tests/cohort.test.mjs
|
||||
6310ea0163c5fcd3f1cbd15e4433ba9c1897e9649ceed1e8e169ed8fb884ad36 packages/conversation/tests/ctrl-child.mjs
|
||||
881121aadcf879a56c84722eec7726b0a71160c58bf99056758dab3d285c514d packages/conversation/tests/fake-pi.mjs
|
||||
b148bb535d6b9727a57feb30b873e84b6a5cc3d849f3c2b0eacf3515b6456d35 packages/conversation/tests/flows.test.mjs
|
||||
28e66a9cc9876660418f4fe171ec51639313ce2ca9675d2e0fdcf7b9c8b5e446 packages/conversation/tests/races.test.mjs
|
||||
b7b5fa60374a55270c5584fff5c97ae2c25ad2056a5569c285c09e78e35752ce packages/conversation/tests/smoke.test.mjs
|
||||
9d0078fa7fa8335d336130a5f0b44f141448e9eb3de59efab1909e50e0243bdb packages/conversation/tests/turns.test.mjs
|
||||
2ce037d9cd480709b31fd20f35c72ee81d04398c7532d44f0ecd9905c5fa9b37 packages/webui/README.md
|
||||
da2ac17666296b954141756fd687652c623560787f03e64b4e43d863b9bfda3d packages/webui/src/bus.mjs
|
||||
06b35c24ef6e25aeeadeef66ffccf090378e5b51d13cf81b04801dba93407f06 packages/webui/src/cli.mjs
|
||||
341a3669b3b45b91a724ea1bc338ab56fda18491db55ac96123f252cb93a6935 packages/webui/src/public/bus.css
|
||||
794c4604dc2b9d0dab5f32e8b44358f3a0f0d7e6682a2cbf393c0f7b42fbb1b8 packages/webui/src/public/bus.js
|
||||
b27d4fbec45f716f2cafee10d8e3abf3be6d2f1701a4f55f52f70dcc9f04fcc4 packages/webui/src/public/index.html
|
||||
2b60c4bca11b3c53d4affb22ce482c9fb0db6b213ffec298473fe899a9e5d026 packages/webui/src/serve.mjs
|
||||
18b1bd786a1e74d554d3bc3de4e0c142203fb43fcd860560bfd6263aa9e6ed11 packages/webui/tests/browser.test.mjs
|
||||
416df2a9a2feca4f1cf9393d092f682eef06850769675b7bb28fc73c05e77010 packages/webui/tests/bus-browser.test.mjs
|
||||
c4ef7ad46824ac85b19b64a1003c82e7585f5a5aea60cc4fc06caee68c6066a1 packages/webui/tests/bus.test.mjs
|
||||
@@ -0,0 +1,27 @@
|
||||
agents/dewey/work/wui/S5-GATE-E.md
|
||||
agents/dewey/work/wui/SLICE1-VIEWS.md
|
||||
agents/dewey/work/wui/TASKS.md
|
||||
agents/dewey/work/wui/evidence/s5-mutants.md
|
||||
docs/plans/DEFERRED.md
|
||||
packages/conversation/README.md
|
||||
packages/conversation/src/cohort.mjs
|
||||
packages/conversation/src/controller.mjs
|
||||
packages/conversation/src/pi-pin.mjs
|
||||
packages/conversation/src/terminal.mjs
|
||||
packages/conversation/tests/cohort.test.mjs
|
||||
packages/conversation/tests/ctrl-child.mjs
|
||||
packages/conversation/tests/fake-pi.mjs
|
||||
packages/conversation/tests/flows.test.mjs
|
||||
packages/conversation/tests/races.test.mjs
|
||||
packages/conversation/tests/smoke.test.mjs
|
||||
packages/conversation/tests/turns.test.mjs
|
||||
packages/webui/README.md
|
||||
packages/webui/src/bus.mjs
|
||||
packages/webui/src/cli.mjs
|
||||
packages/webui/src/public/bus.css
|
||||
packages/webui/src/public/bus.js
|
||||
packages/webui/src/public/index.html
|
||||
packages/webui/src/serve.mjs
|
||||
packages/webui/tests/browser.test.mjs
|
||||
packages/webui/tests/bus-browser.test.mjs
|
||||
packages/webui/tests/bus.test.mjs
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
cd ~/darkwing-scratch/r40/wt
|
||||
export TMPDIR=~/darkwing-scratch/tmp DOCKER_HOST=unix:///nonexistent.sock
|
||||
O=../out; : > $O/summary.txt
|
||||
for p in webui conversation control-board; do
|
||||
s=$(date +%s); node --test "packages/$p/tests/*.test.mjs" > $O/node-$p.txt 2>&1; rc=$?
|
||||
echo "node-$p rc=$rc $(grep -E '^ℹ (pass|fail)' $O/node-$p.txt | tr '\n' ' ') $(( $(date +%s)-s ))s" >> $O/summary.txt
|
||||
done
|
||||
for t in scripts/test-*.sh; do n=$(basename $t .sh)
|
||||
s=$(date +%s); bash $t > $O/$n.txt 2>&1; rc=$?
|
||||
echo "$n rc=$rc $(tail -3 $O/$n.txt | tr '\n' ' ' | cut -c1-160) $(( $(date +%s)-s ))s" >> $O/summary.txt
|
||||
done
|
||||
echo DONE >> $O/summary.txt
|
||||
@@ -0,0 +1,215 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (215.734217ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (30.090989ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.980164ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (405.386156ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (374.318183ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (240.091944ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (192.837364ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (200.373425ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.247794ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9753.675015ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28405.238599ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (397.68177ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (206.523203ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (413.50365ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (282.600014ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (367.309205ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (184.483417ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (246.472104ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (376.069017ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (769.132946ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (196.401677ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (93.585559ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (81.210759ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.601222ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.069301ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.906818ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2952.064699ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (408.438337ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2710.368338ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4765.598324ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2461.207352ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2431.554867ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2426.758587ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4972.26544ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (830.798916ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (649.094567ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (457.343009ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (714.834561ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (363.544463ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (449.242512ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3226.990201ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (21.931046ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (358.679006ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (319.741504ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (92.571552ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (201.562548ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (190.800832ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (239.813323ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (266.529084ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (194.14995ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.42812ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (197.036991ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (296.310125ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (206.808005ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (185.768088ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (207.101203ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (212.435542ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.600455ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (207.322116ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (226.364403ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (228.214604ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (193.702896ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.387882ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.264308ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.319035ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.461003ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.46042ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.334847ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.096673ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (221.293496ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (250.648465ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (330.562218ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (787.229564ms)
|
||||
✔ H4: self-takeover is refused (192.278662ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (421.586417ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1035.200957ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (218.074312ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (644.738076ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (265.478844ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (176.351176ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (183.107693ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (544.024946ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (276.417652ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (179.128911ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (806.617395ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (296.643113ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (781.424447ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (199.831614ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (342.352883ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.704008ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (650.602629ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (531.111718ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (887.14627ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3008.033112ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (596.639978ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.764637ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.641242ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.714388ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.442191ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.541866ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.954164ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.985548ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.86867ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.834017ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.021926ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.020142ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.808422ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.43336ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.43153ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.503466ms)
|
||||
✔ F9: registrations never add or redirect a root (2.010541ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.797972ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.850422ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.213076ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.719771ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.2892ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.763214ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (552.473465ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.27795ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.514904ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.288278ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.119264ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.946767ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (745.419524ms)
|
||||
✔ the engine pin holds for the installed package (2.795764ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (309.928745ms)
|
||||
✖ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (282.865427ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (258.878451ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (523.940095ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (407.494234ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (237.379859ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.912765ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (345.240285ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (201.603905ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (285.650341ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (465.982865ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1880.98028ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (179.733316ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (266.134272ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (179.524672ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (201.15844ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (335.715067ms)
|
||||
✔ N10: fake conformance (32.391103ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (407.037914ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (272.04392ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (275.591711ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (327.502102ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (376.373895ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (379.53785ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (201.832666ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (326.128546ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (653.07212ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (557.478341ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (647.661691ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (230.230467ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (197.294388ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (205.026807ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (223.870077ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (223.608242ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 158
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 45394.243997
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/smoke.test.mjs:168:1
|
||||
✖ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (282.865427ms)
|
||||
AssertionError [ERR_ASSERTION]: the project skill is not loaded under the seal
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'llama',
|
||||
+ 'skill:probe',
|
||||
+ 'skill:amazon-bedrock',
|
||||
+ 'skill:aws-ai-ml',
|
||||
+ 'skill:aws-auth',
|
||||
+ 'skill:aws-billing-and-cost-management',
|
||||
+ 'skill:aws-blocks',
|
||||
+ 'skill:aws-cdk',
|
||||
+ 'skill:aws-cloudformation',
|
||||
+ 'skill:aws-compute',
|
||||
+ 'skill:aws-containers',
|
||||
+ 'skill:aws-database',
|
||||
+ 'skill:aws-deployment',
|
||||
+ 'skill:aws-iam',
|
||||
+ 'skill:aws-messaging-and-streaming',
|
||||
+ 'skill:aws-networking',
|
||||
+ 'skill:aws-observability',
|
||||
+ 'skill:aws-sdk-js-v3-usage',
|
||||
+ 'skill:aws-sdk-python-usage',
|
||||
+ 'skill:aws-sdk-swift-usage',
|
||||
+ 'skill:aws-security',
|
||||
+ 'skill:aws-serverless',
|
||||
+ 'skill:aws-storage',
|
||||
+ 'skill:diagnose-crash',
|
||||
+ 'skill:launch-with-aws',
|
||||
+ 'skill:signing-in-to-aws'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/smoke.test.mjs:175: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: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ 'llama', 'skill:probe', 'skill:amazon-bedrock', 'skill:aws-ai-ml', 'skill:aws-auth', 'skill:aws-billing-and-cost-management', 'skill:aws-blocks', 'skill:aws-cdk', 'skill:aws-cloudformation', 'skill:aws-compute', 'skill:aws-containers', 'skill:aws-database', 'skill:aws-deployment', 'skill:aws-iam', 'skill:aws-messaging-and-streaming', 'skill:aws-networking', 'skill:aws-observability', 'skill:aws-sdk-js-v3-usage', 'skill:aws-sdk-python-usage', 'skill:aws-sdk-swift-usage', 'skill:aws-security', 'skill:aws-serverless', 'skill:aws-storage', 'skill:diagnose-crash', 'skill:launch-with-aws', 'skill:signing-in-to-aws' ],
|
||||
expected: [ 'llama' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (197.03779ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (29.97602ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.555786ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (352.033926ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (390.75591ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (267.856413ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (233.513447ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (233.453037ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.478252ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10100.231669ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28958.101561ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (433.739704ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (155.090042ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (380.413215ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (273.553989ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (340.215515ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (101.794584ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (176.0337ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (262.954923ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (719.88442ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (182.272223ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (85.297948ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (75.006333ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.83732ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.086378ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.957084ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2912.34442ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (466.917263ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2860.450314ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4802.535499ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2542.576909ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2579.387986ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2345.04178ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4955.366077ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (649.392469ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (510.796316ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (412.605052ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (640.124815ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (355.984153ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (464.198228ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3296.679746ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (41.119379ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (371.099757ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (295.684511ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.111489ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (192.850763ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (192.39652ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (190.623196ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (282.322192ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (188.384605ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.460758ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (184.709668ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (352.168356ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (227.232449ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (201.331356ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (211.051472ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (209.300908ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.524658ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (207.326016ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (225.824307ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (253.245799ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (183.629919ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.402648ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.292537ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.313979ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.478511ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.549054ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.35613ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.106707ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (229.196585ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (243.695057ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (273.202709ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (883.270169ms)
|
||||
✔ H4: self-takeover is refused (203.835722ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (448.37835ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1018.697608ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (260.023243ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (942.559029ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (329.264518ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (187.90565ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (185.737917ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (567.005149ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (283.411765ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (184.54151ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (846.58303ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (345.55871ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (943.931786ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (213.609299ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (343.860754ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.6235ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (670.297241ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (561.154597ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (843.198903ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3047.048478ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (710.567097ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.413692ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.527954ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.557562ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.359374ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.555265ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.028353ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.942922ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.205415ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.455034ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.160185ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.781395ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.290279ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.078973ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.223607ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.729329ms)
|
||||
✔ F9: registrations never add or redirect a root (1.545998ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.620291ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.641044ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.415534ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.789138ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.2822ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.688824ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (516.621654ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.000039ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.448373ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.321526ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.046598ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.887048ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (786.047611ms)
|
||||
✔ the engine pin holds for the installed package (1.751146ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (260.447681ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (536.229231ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (263.288171ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (466.411929ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (420.556417ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (226.043858ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.236259ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (416.227922ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (217.403283ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (303.067315ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (479.70206ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1926.323545ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (198.725403ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (259.460009ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (190.678301ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (223.731366ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (375.925123ms)
|
||||
✔ N10: fake conformance (31.869748ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (363.722004ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (263.081325ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (283.560756ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (367.415706ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (352.779208ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (367.598488ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (209.744524ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (516.089118ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (780.022393ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (554.569781ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (587.382581ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (317.258867ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (299.819146ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (229.222486ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (284.282632ms)
|
||||
✖ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (409.522865ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 158
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 45916.46308
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/turns.test.mjs:985:1
|
||||
✖ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (409.522865ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected rejection.
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/turns.test.mjs:1034: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: undefined,
|
||||
operator: 'rejects',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (214.095609ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.345601ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (23.735595ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (351.524729ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (382.804946ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (241.043126ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (198.032439ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (192.073591ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.22368ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9852.701624ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28569.705097ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (258.673948ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (143.122799ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (381.512542ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (274.589497ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (339.816735ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (103.896181ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (169.686307ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (264.287608ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (708.710667ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (193.137923ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (89.60988ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (74.367017ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.377474ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.120036ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.008147ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2971.800289ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (407.700041ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2764.545424ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4827.245221ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2565.568268ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2482.612751ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2357.066443ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4865.368147ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (688.182849ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (551.850357ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (432.364624ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (611.43579ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (333.974321ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (487.726516ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3278.207669ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (24.167348ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (364.029384ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (385.457513ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.643827ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (203.232558ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (206.104347ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (197.937513ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (279.71601ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (199.451891ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.431411ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (201.951872ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (303.058582ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (215.256675ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (167.885224ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (185.48778ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (200.589464ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.138366ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (195.564651ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (210.607897ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (233.096676ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (190.11544ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.423484ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.264841ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.325254ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.448962ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.975494ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.378942ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.10795ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (220.696141ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (246.211131ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (293.920118ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (797.551106ms)
|
||||
✔ H4: self-takeover is refused (194.259238ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (406.913821ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (949.742318ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (172.45756ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (734.159675ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (286.265296ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (179.925063ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (152.422798ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (567.960789ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (327.231841ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (302.277385ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1049.972982ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (372.856995ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (896.710868ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (258.952819ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (374.877178ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.648685ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (685.93ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (518.583469ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (679.093781ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3043.848224ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (633.335568ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.511269ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.665091ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.69395ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.53559ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.555307ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.923282ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.777207ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.283103ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.911031ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.93761ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.884498ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.649865ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.999428ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.016961ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.949543ms)
|
||||
✔ F9: registrations never add or redirect a root (1.649819ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.011248ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.761747ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.141885ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.304809ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.246194ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.696235ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (543.810968ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.291317ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.722768ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.526017ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.217081ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.272513ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (798.712911ms)
|
||||
✔ the engine pin holds for the installed package (2.056258ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (285.457126ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (543.939009ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (236.447859ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (483.951474ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (438.600149ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (237.148269ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.112248ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (352.447961ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (194.763483ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (282.100367ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (438.162852ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1849.119502ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (151.701627ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (270.339359ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (185.743933ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (219.330312ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (359.946996ms)
|
||||
✔ N10: fake conformance (32.57973ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (624.636357ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (342.1363ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (294.692455ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (418.611803ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (363.965179ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (477.43516ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (242.623372ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (434.41769ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (698.625644ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (545.441811ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (484.142656ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (215.795047ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (172.761112ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (206.988124ms)
|
||||
✖ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (23.73934ms)
|
||||
✖ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (1.620028ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 157
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 45029.401862
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/turns.test.mjs:921:1
|
||||
✖ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (23.73934ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/turns.test.mjs:945:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/turns.test.mjs:985:1
|
||||
✖ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (1.620028ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception: {"command":"/bin/sh"}
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/turns.test.mjs:991:12)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: undefined,
|
||||
operator: 'throws',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (274.192438ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.689842ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.63745ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (366.877011ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (411.519471ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (255.931163ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (194.522528ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (193.476597ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.422065ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10155.345335ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28162.143547ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (251.876892ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (143.568637ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (397.488732ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (277.092965ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (327.790274ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (105.142762ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (165.04157ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (257.465448ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (737.106075ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (242.251611ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (100.426616ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (75.785877ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.111719ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.085039ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.982371ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2955.12095ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (435.280165ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2727.345945ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5008.827865ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2482.574522ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2496.621139ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2375.214718ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4928.028497ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (707.75689ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (547.814058ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (399.271979ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (672.398112ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (337.744689ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (579.693891ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3318.731ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (38.466597ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (387.555766ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (278.546021ms)
|
||||
✖ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (1.301895ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (256.091094ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (210.352845ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (190.964018ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (297.030046ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (204.39464ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.791872ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (195.961061ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (297.503976ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (210.02514ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (161.981728ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (200.498098ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (220.311749ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.131797ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (223.150912ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (256.354182ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (253.807238ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (203.860132ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.43712ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.27438ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.317879ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.498648ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.54897ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.364944ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.099395ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (232.886748ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (260.300216ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (307.298538ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (820.404169ms)
|
||||
✔ H4: self-takeover is refused (176.668858ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (420.993534ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1084.515517ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (211.081944ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (678.69221ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (336.763525ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (173.868041ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (163.28455ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (606.076245ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (467.101512ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (277.934696ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (941.794372ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (376.499325ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (767.146772ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (196.411932ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (389.350727ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.678811ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (970.710054ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (567.939924ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (725.921342ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3020.57529ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (587.290166ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.51236ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.108435ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.226641ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (4.035531ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.819874ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.538248ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.88837ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.301627ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.543424ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.959544ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (6.623724ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.926679ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (9.078904ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (10.296415ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (3.15002ms)
|
||||
✔ F9: registrations never add or redirect a root (2.556755ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.320427ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (1.318759ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (7.620597ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (3.266172ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.474117ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.86065ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (565.696933ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.899549ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.616405ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.418204ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.128571ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.100197ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (888.735189ms)
|
||||
✔ the engine pin holds for the installed package (2.150149ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (325.265909ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (552.918625ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (304.379471ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (542.190667ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (451.337043ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (234.74496ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.606335ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (352.580901ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (189.427908ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (267.691098ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (488.043922ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1896.623699ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (151.669622ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (260.837621ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (209.82217ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (231.281658ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (513.233476ms)
|
||||
✔ N10: fake conformance (31.21414ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (618.72897ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (309.097268ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (300.714733ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (376.738117ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (359.591639ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (365.570073ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (208.631343ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (381.470632ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (1076.847028ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (566.105131ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (484.320593ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (214.469601ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (167.90282ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (206.575547ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (215.856945ms)
|
||||
✖ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (226.607668ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 157
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 45108.355323
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/cohort.test.mjs:719:1
|
||||
✖ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (1.301895ms)
|
||||
AssertionError [ERR_ASSERTION]: XDG_RUNTIME_DIR
|
||||
|
||||
true !== false
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/cohort.test.mjs:727:53)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: true,
|
||||
expected: false,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/turns.test.mjs:985:1
|
||||
✖ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (226.607668ms)
|
||||
AssertionError [ERR_ASSERTION]: NODE_TEST_CONTEXT
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/turns.test.mjs:1020:48)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: false,
|
||||
expected: true,
|
||||
operator: '==',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (252.01439ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (31.489523ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.531414ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (366.215517ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (385.352696ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (231.941266ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (210.492776ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (180.157794ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.279785ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9729.362328ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28655.546457ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (250.617713ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (150.579649ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (380.336628ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (267.082472ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (336.957538ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (101.924094ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (168.9722ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (260.781709ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (725.285009ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (177.208335ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (87.331132ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (70.816601ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.571417ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.11596ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.734854ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2956.430273ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (409.614268ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2713.875705ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4772.108131ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2485.76993ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2530.682957ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2414.026802ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5008.08648ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (742.611065ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (572.755392ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (434.882205ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (690.023686ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (388.402734ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (487.574544ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3414.405647ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (117.200394ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (484.087662ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (634.720767ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.525567ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (193.32442ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (204.610977ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (239.457405ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (287.773019ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (197.173178ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.413161ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (199.287718ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (287.179408ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (228.510474ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (179.814703ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (206.257005ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (204.046065ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.599427ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (202.075965ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (214.050858ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (245.59034ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (198.757365ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.397968ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.266243ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.31703ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.489405ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.548117ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.373164ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.102205ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (222.830689ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (290.058098ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (296.338706ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (798.243374ms)
|
||||
✔ H4: self-takeover is refused (208.279821ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (423.421109ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (993.866535ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (196.155859ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (632.245802ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (321.916355ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (191.520939ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (169.200574ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (591.108818ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (278.13812ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (202.312162ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (878.598068ms)
|
||||
✖ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (196.845024ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (729.917774ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (252.641858ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (462.178583ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.523346ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (726.36511ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (600.861984ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (703.121301ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2945.075111ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (639.989098ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.679214ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.469022ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.544347ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.358444ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.620177ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.829453ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.722721ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.23306ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.213235ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.709143ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.977509ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.606156ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.067208ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.759639ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.041497ms)
|
||||
✔ F9: registrations never add or redirect a root (1.458751ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.160638ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.75834ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.253724ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.226052ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.195277ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.734529ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (506.591055ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.979408ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.61534ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.373404ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.124348ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.131518ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (755.026327ms)
|
||||
✔ the engine pin holds for the installed package (1.692423ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (257.158334ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (524.821012ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (255.164389ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (522.214615ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (429.438467ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (241.03602ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.698651ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (336.598921ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (207.273528ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (294.178409ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (464.491287ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1869.316966ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (146.982051ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (280.041726ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (194.504097ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (230.775729ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (361.427403ms)
|
||||
✔ N10: fake conformance (31.716838ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (409.329857ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (273.861818ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (294.53304ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (359.416715ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (370.163451ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (325.826136ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (232.25554ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (491.732564ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (875.906995ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (620.599674ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (497.151605ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (208.605161ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (178.75738ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (156.790084ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (232.495923ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (206.189095ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 158
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 45275.981581
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/races.test.mjs:566:1
|
||||
✖ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (196.845024ms)
|
||||
AssertionError [ERR_ASSERTION]: the flag is cleared on the throw
|
||||
+ actual - expected
|
||||
|
||||
+ 'stop-559e6aca9b4abf45'
|
||||
- null
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/races.test.mjs:576: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: 'stop-559e6aca9b4abf45',
|
||||
expected: null,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (208.379184ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.708965ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.722303ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (367.079952ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (441.831479ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (245.123658ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (189.196636ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (249.210469ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.222024ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9941.485877ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28925.986271ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (271.97681ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (145.588987ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (388.140706ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (308.571438ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (360.624944ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (104.346068ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (180.472853ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (264.795244ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (739.344879ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (411.085238ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (174.028461ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (69.586299ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.468875ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.087087ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.767224ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2947.908413ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (408.226994ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2685.443746ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4798.337116ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2529.760773ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2512.012952ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2385.803026ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5033.678846ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (769.956365ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (549.920737ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (425.141876ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (604.023087ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (358.564082ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (449.67424ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3296.129692ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (29.468267ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (329.987821ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (295.713833ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (81.312792ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (192.150114ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (204.217747ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (197.72356ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (355.282622ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (200.773264ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.41759ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (196.15806ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (290.302122ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (288.111081ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (213.499099ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (210.614328ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (211.424395ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.706419ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (208.422337ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (225.76497ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (231.058219ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (198.744301ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.356207ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.24642ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.325815ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.446664ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.502883ms)
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.33097ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.102764ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (238.257033ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (257.085596ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (299.470685ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (859.099561ms)
|
||||
✔ H4: self-takeover is refused (276.975406ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (467.86615ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1011.461007ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (190.080357ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (663.247233ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (287.512063ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (193.629884ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (173.814805ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (586.425474ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (300.488496ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (212.724974ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (897.548461ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (366.981315ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (780.998686ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (316.194283ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (451.056112ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.595911ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (771.973126ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (547.094588ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (689.545133ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2971.626798ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (617.550263ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.705826ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.517099ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.439911ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.143675ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.501837ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.733036ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.683251ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.082298ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.074295ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.571199ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.22585ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.434529ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.179928ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.194749ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.512069ms)
|
||||
✔ F9: registrations never add or redirect a root (1.425923ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.689045ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.698645ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.44308ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.742653ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.19793ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.700578ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (510.072972ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.051947ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.531414ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.438218ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.101352ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.956673ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (771.646559ms)
|
||||
✔ the engine pin holds for the installed package (1.666342ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (257.584691ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (532.755669ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (273.964511ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (476.056493ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (521.50693ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (240.131647ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.882703ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (343.565958ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (274.046792ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (343.965533ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (491.17782ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1832.644239ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (174.790434ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (278.310176ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (192.452376ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (227.414947ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (352.357668ms)
|
||||
✔ N10: fake conformance (31.920062ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (416.377276ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (290.413117ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (297.164374ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (377.402401ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (360.14ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (345.048973ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (246.784167ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (554.479285ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (920.620683ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (548.884553ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (513.57861ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (199.649854ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (173.852295ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (198.324393ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (256.703598ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (234.614341ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 158
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 45906.210956
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:695:1
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.33097ms)
|
||||
Error: not connected
|
||||
at stub.takeover (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/flows.test.mjs:701:28)
|
||||
at #act (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/src/terminal.mjs:206:80)
|
||||
at file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/src/terminal.mjs:193:37
|
||||
at #run (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/src/terminal.mjs:132:42)
|
||||
at file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/src/terminal.mjs:111:49
|
||||
at async waitForActual (node:assert:615:5)
|
||||
at async strict.rejects (node:assert:738:25)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/flows.test.mjs:705:3)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
|
||||
@@ -0,0 +1,212 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (296.879466ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.602049ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.988722ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (634.77033ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (453.736192ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (252.635706ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (207.837078ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (246.981625ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.20387ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10701.199275ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28622.102803ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (255.657875ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (142.962745ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (373.96892ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (273.544328ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (343.973996ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (106.330855ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (177.406761ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (272.637294ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (727.233609ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (195.085291ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (97.562888ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (72.519482ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.174078ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.070535ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.848447ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3269.439191ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (444.526081ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2779.973182ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4990.707418ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2602.780023ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2523.308505ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2355.186127ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5053.411674ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (714.496723ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (564.706305ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (577.380937ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (661.878683ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (366.946461ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (557.442049ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3290.493335ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (24.581982ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (376.593608ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (271.967232ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.577497ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (298.450359ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (219.778174ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (435.308373ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (377.306704ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (208.797517ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.421764ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (226.260973ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (325.624776ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (254.701092ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (249.170151ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (225.932826ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (239.512549ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.773179ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (229.954779ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (241.043249ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (268.968065ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (221.082673ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.391363ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.275444ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.318012ms)
|
||||
✖ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (1.048883ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.38194ms)
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.417655ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.108076ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (226.913325ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (263.394406ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (569.967493ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (905.563084ms)
|
||||
✔ H4: self-takeover is refused (234.183058ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (517.222561ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1125.986535ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (219.559426ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (716.109466ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (333.659671ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (216.405515ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (198.95763ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (642.435178ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (319.532164ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (225.456339ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (978.006103ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (468.487843ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1216.293202ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (235.150364ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (373.41183ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.694791ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (692.751101ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (557.237066ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (726.214256ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3075.482023ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (614.452576ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.735959ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.850053ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.901921ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.66378ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.606899ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.120974ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.794561ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.319727ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.680396ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.255977ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.929874ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.998962ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.120789ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.957622ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.729244ms)
|
||||
✔ F9: registrations never add or redirect a root (1.649047ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.348808ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.780672ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.913409ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.097609ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.327085ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.81907ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (557.254154ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.98921ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.683243ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.338516ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.071919ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.93729ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (834.056181ms)
|
||||
✔ the engine pin holds for the installed package (2.722366ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (268.4409ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (540.509377ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (287.689775ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (611.026628ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (764.388387ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (264.467228ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.700682ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (387.715704ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (238.933065ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (359.666641ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (522.709088ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1904.764961ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (210.184241ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (302.764591ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (210.628946ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (252.47441ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (373.828393ms)
|
||||
✔ N10: fake conformance (32.105439ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (459.654921ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (316.01937ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (303.206479ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (472.412874ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (428.917428ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (699.491437ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (282.746565ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (418.366953ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (822.805927ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (583.639915ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (439.183085ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (225.307202ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (250.532768ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (249.623854ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (286.109959ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (270.012875ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 157
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 46457.536737
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:622:1
|
||||
✖ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (1.048883ms)
|
||||
AssertionError [ERR_ASSERTION]: text typed after the takeover is sent
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'hi'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/flows.test.mjs:629: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: [ 'hi' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:695:1
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.417655ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'hi'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/flows.test.mjs:710: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: [ 'hi' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (211.092981ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.733864ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.932717ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (375.797497ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (472.496544ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (268.375073ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (246.287921ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (275.581255ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.69412ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10336.184958ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28721.630848ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (425.153978ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (160.074522ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (440.44504ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (305.543379ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (404.469792ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (109.073704ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (173.031065ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (277.457478ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (805.018807ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (176.510769ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (94.845238ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (78.852164ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.141458ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.101538ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.719039ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3010.438173ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (430.9459ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2757.697662ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5268.048614ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2519.614996ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2517.187317ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2319.730115ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4843.065359ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (660.454044ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (525.144915ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (420.896538ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (612.547653ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (407.607433ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (481.50494ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3276.206316ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (39.317371ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (346.907107ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (330.257916ms)
|
||||
✖ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (2.884999ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (201.115283ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (211.570095ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (214.702081ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (352.014843ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (194.613313ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.679594ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (196.906002ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (338.928015ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (278.039565ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (210.97132ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (219.305896ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (214.376273ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.963602ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (202.905996ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (221.073745ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (239.463364ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (202.961559ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.394422ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.257447ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.313652ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (1.054752ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.917936ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.363857ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.096568ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (231.352165ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (275.481809ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (382.020762ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (927.184305ms)
|
||||
✔ H4: self-takeover is refused (227.307647ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (468.290263ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1000.172793ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (213.939092ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (685.167323ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (349.076018ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (199.478283ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (188.729884ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (805.941736ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (298.547727ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (259.606662ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1251.319396ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (511.769523ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (815.430781ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (202.535553ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (352.883403ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.711028ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (614.292922ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (558.283096ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (883.075575ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2899.722981ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (633.046632ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.73204ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.524952ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.523678ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.322127ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.578563ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.83161ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.72625ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.183773ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.200172ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.520068ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.497767ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.36033ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.294169ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.372157ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.608869ms)
|
||||
✔ F9: registrations never add or redirect a root (1.500659ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.640168ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.640996ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.366858ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.657602ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.226544ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.681958ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (519.484827ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.317123ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.597311ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.391469ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.143826ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.048682ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (792.211447ms)
|
||||
✔ the engine pin holds for the installed package (1.628098ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (247.561343ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (538.434901ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (322.058507ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (494.848905ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (514.150432ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (230.188919ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.012945ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (406.680056ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (276.494037ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (315.427162ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (477.595467ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1888.222153ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (177.586665ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (276.824034ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (196.082661ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (342.81011ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (498.763098ms)
|
||||
✔ N10: fake conformance (32.883332ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (538.082625ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (313.846605ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (473.666685ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (501.069651ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (519.887083ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (375.125152ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (217.500393ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (386.398391ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (692.048111ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (555.910759ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (436.527312ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (198.540286ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (208.571146ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (145.604125ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (180.784214ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (172.227126ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 158
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 46344.419184
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/cohort.test.mjs:719:1
|
||||
✖ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (2.884999ms)
|
||||
Error: the scope exited before the shim answered ({"code":1,"signal":null})
|
||||
at ScopeLauncher.launch (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/src/cohort.mjs:113:45)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/conversation/tests/cohort.test.mjs:729:16)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
code: 'launch-failed'
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3221.628233ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4263.755917ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5119.347359ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (252.958033ms)
|
||||
✖ bus routes are GET-only, have no write verb, and keep the same-origin checks (14.662257ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (65.376398ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2449.7778ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (165.763824ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3079.004277ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2476.049333ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1210.712061ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (851.102727ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4024.752782ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53895.302206ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4236.558657ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21613.242497ms)
|
||||
✔ loopback host and board origin fail closed (8.09759ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (77.928439ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (45.822306ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1338.880388ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 19
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54187.939054
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:88:1
|
||||
✖ bus routes are GET-only, have no write verb, and keep the same-origin checks (14.662257ms)
|
||||
AssertionError [ERR_ASSERTION]: POST
|
||||
|
||||
200 !== 405
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/webui/tests/bus.test.mjs:94:77)
|
||||
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,54 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3073.071578ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3189.998512ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (3868.811802ms)
|
||||
✖ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (234.334354ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (95.409477ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (97.94629ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2892.139712ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (108.562253ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3141.473487ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1490.775134ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1058.979921ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (796.20488ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2633.758473ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53209.783204ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2979.733343ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22024.39633ms)
|
||||
✔ loopback host and board origin fail closed (9.045907ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (96.127964ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (85.806072ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1487.792561ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 19
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53607.640805
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:47:1
|
||||
✖ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (234.334354ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'',
|
||||
+ 'invalid-response'
|
||||
- 'invalid-request'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/webui/tests/bus.test.mjs:83:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
|
||||
generatedMessage: true,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ '', 'invalid-response' ],
|
||||
expected: [ '', 'invalid-request' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3803.143211ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4150.99418ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5513.812675ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (293.93593ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (118.151082ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (75.472187ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2747.214028ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (144.717044ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3841.377464ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1741.194052ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1126.155233ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (820.523132ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4036.515655ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53920.32752ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4020.396638ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22048.307214ms)
|
||||
✔ loopback host and board origin fail closed (7.477996ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (94.638418ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (78.017455ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1504.625696ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 20
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54272.538263
|
||||
@@ -0,0 +1,50 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3903.591086ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4463.702877ms)
|
||||
✖ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4518.981424ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (279.019779ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (159.736142ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (58.470909ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2488.423873ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (249.447116ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4493.182334ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1537.330523ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1091.121132ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (786.66719ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4450.612777ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54196.285951ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4245.335109ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21797.08746ms)
|
||||
✔ loopback host and board origin fail closed (7.038814ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (77.894089ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (43.066275ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1046.053579ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 19
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54466.262774
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus-browser.test.mjs:68:1
|
||||
✖ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4518.981424ms)
|
||||
AssertionError [ERR_ASSERTION]: decision
|
||||
|
||||
2 !== 0
|
||||
|
||||
at inertCheck (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/webui/tests/bus-browser.test.mjs:79:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/webui/tests/bus-browser.test.mjs:133: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: 2,
|
||||
expected: 0,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4264.624684ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4915.776488ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5506.106252ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (302.974369ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (164.739853ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (301.289466ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2336.450303ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (146.099874ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4159.684661ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2486.788222ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1379.50928ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (888.468817ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3816.895237ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54704.988605ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4035.41478ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22152.655801ms)
|
||||
✔ loopback host and board origin fail closed (6.827332ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (71.673018ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (41.736535ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1256.131211ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 20
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 61908.886122
|
||||
@@ -0,0 +1,52 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3371.478092ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3533.766582ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5236.546106ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (265.403013ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (218.700345ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (77.297046ms)
|
||||
✖ humanCall speaks the human transport protocol without blocking the server (987.672419ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (338.959782ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3347.152936ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2067.554963ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1139.050408ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (846.901724ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3092.438272ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53731.708381ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3145.795761ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22022.168906ms)
|
||||
✔ loopback host and board origin fail closed (9.381138ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (92.056158ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (76.38614ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1449.133125ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 19
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54092.092299
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:146:1
|
||||
✖ humanCall speaks the human transport protocol without blocking the server (987.672419ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'resolved'
|
||||
- 'response-too-large'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/webui/tests/bus.test.mjs:154: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: 'resolved',
|
||||
expected: 'response-too-large',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4078.025266ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4229.110588ms)
|
||||
✖ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (10593.488837ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (278.884475ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (126.572093ms)
|
||||
✖ bus refusals map to statuses and never carry a path or a stack (19.130718ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2702.097792ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (451.218777ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4085.471214ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1874.890628ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1186.036497ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1060.494636ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4043.986396ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54810.893037ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4042.638709ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21982.440883ms)
|
||||
✔ loopback host and board origin fail closed (6.288103ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (92.036631ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (112.29199ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1394.064416ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 18
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 55233.638745
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus-browser.test.mjs:68:1
|
||||
✖ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (10593.488837ms)
|
||||
Error: Uncaught (in promise) Error: Condition timed out: document.querySelector("#bus-view h1").textContent==="Bus refused the read": undefined
|
||||
at Object.evaluate (file:///home/jwoltje/darkwing-scratch/r40/wt/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/r40/wt/packages/webui/tests/bus-browser.test.mjs:235:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:104:1
|
||||
✖ bus refusals map to statuses and never carry a path or a stack (19.130718ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'human-required',
|
||||
+ 502,
|
||||
- 403,
|
||||
'human-required'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40/wt/packages/webui/tests/bus.test.mjs:115: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: [ 'human-required', 502, 'human-required' ],
|
||||
expected: [ 'human-required', 403, 'human-required' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4692.208792ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5111.336662ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5354.257314ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (292.823804ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (88.625651ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (45.995245ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2413.458142ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (204.764008ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4308.079893ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1993.341774ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1187.251099ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (760.485733ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4301.813666ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54092.039906ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4340.487534ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22566.802883ms)
|
||||
✔ loopback host and board origin fail closed (8.882657ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (103.083919ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (91.974333ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1290.735571ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 20
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54463.595654
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
cd ~/darkwing-scratch/r40/mut; : > summary.txt
|
||||
for m in Ma-no-approve Mb-seal-command Mc-engine-keys Md-env-inherit Me-escalating Mf-queue-catch Mg-no-hold Mh-manager-env; do bash run.sh $m conversation; done
|
||||
for m in Mi-bus-post Mj-subject Mk-copy-safe Ml-option-inert Mm-timeout-kill Mn-max-bytes Mo-status Mp-business-id; do bash run.sh $m webui; done
|
||||
cd ../wt && sha256sum -c ../candidate-manifest-r2.sha256 2>&1 | grep -c ': OK$' > ../mut/manifest-after.txt
|
||||
echo DONE >> ../mut/summary.txt
|
||||
@@ -0,0 +1 @@
|
||||
27
|
||||
@@ -0,0 +1,27 @@
|
||||
# mutate.py <id>: apply one named mutant (exact text, must match once). Prints the file.
|
||||
import sys
|
||||
C = "packages/conversation/src/"; W = "packages/webui/src/"
|
||||
M = {
|
||||
"Ma-no-approve": (C + "pi-pin.mjs", '"--no-themes", "--no-approve"]', '"--no-themes"]'),
|
||||
"Mb-seal-command": (C + "controller.mjs", "if (this.engine.sealed && (", "if (false && ("),
|
||||
"Mc-engine-keys": (C + "controller.mjs", "if (extra !== undefined) throw", "if (false) throw"),
|
||||
"Md-env-inherit": (C + "pi-pin.mjs", ' for (const k of [...ENGINE_ENV, ...envKeys]) if (typeof source[k] === "string") env[k] = source[k];', " Object.assign(env, source);"),
|
||||
"Me-escalating": (C + "controller.mjs", " if (this.escalating === s.id) this.escalating = null;\n", ""),
|
||||
"Mf-queue-catch": (C + "terminal.mjs", "this.queue.catch(() => {}).then(", "this.queue.then("),
|
||||
"Mg-no-hold": (C + "terminal.mjs", " this.held = s.slice(i + 1);\n break;\n", ""),
|
||||
"Mh-manager-env": (C + "cohort.mjs", "env: { ...manager, ...env }", "env"),
|
||||
"Mi-bus-post": (W + "serve.mjs", " if (req.method !== 'GET') return json(res, 405, { error: 'method not allowed' });\n return busRead", " return busRead"),
|
||||
"Mj-subject": (W + "serve.mjs", "if (!subjectOk(subject))", "if (false)"),
|
||||
"Mk-copy-safe": (W + "public/bus.js", "SAFE.test(String(d.id)) && SAFE.test(String(o.key))", "true"),
|
||||
"Ml-option-inert": (W + "public/bus.js", "${txt(o.text)}", "${o.text}"),
|
||||
"Mm-timeout-kill": (W + "bus.mjs", "const timer = setTimeout(() => { child.kill('SIGKILL'); done(", "const timer = setTimeout(() => { done("),
|
||||
"Mn-max-bytes": (W + "bus.mjs", "if (size > maxBytes)", "if (false)"),
|
||||
"Mo-status": (W + "bus.mjs", "'human-required': 403,", "'human-required': 502,"),
|
||||
"Mp-business-id": (W + "cli.mjs", "if (!ID_PATTERN.test(value)) throw", "if (false) throw"),
|
||||
}
|
||||
f, old, new = 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(f)
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
# run.sh <mutant> <pkg>: back up, mutate, run the package's node suite, restore from the backup.
|
||||
set -u
|
||||
cd ~/darkwing-scratch/r40/wt
|
||||
export TMPDIR=~/darkwing-scratch/tmp
|
||||
m=$1; p=$2
|
||||
f=$(python3 -c "import sys; sys.argv=['x','$m']; exec(open('../mut/mutate.py').read().split('f, old, new')[0]); print(M['$m'][0])")
|
||||
cp -p "$f" ../mut/bak/current
|
||||
python3 ../mut/mutate.py "$m" >/dev/null || { cp -p ../mut/bak/current "$f"; echo "$m: no match" | tee -a ../mut/summary.txt; exit 1; }
|
||||
out=../mut/$m-$p.txt
|
||||
timeout 600 node --test "packages/$p/tests/*.test.mjs" > "$out" 2>&1; rc=$?
|
||||
cp -p ../mut/bak/current "$f"
|
||||
pass=$(grep -E '^ℹ pass' "$out" | awk '{print $3}'); fail=$(grep -E '^ℹ fail' "$out" | awk '{print $3}')
|
||||
echo "$m ($f): $p rc $rc pass $pass fail $fail" | tee -a ../mut/summary.txt
|
||||
@@ -0,0 +1,17 @@
|
||||
Ma-no-approve (packages/conversation/src/pi-pin.mjs): conversation rc 1 pass 158 fail 1
|
||||
Mb-seal-command (packages/conversation/src/controller.mjs): conversation rc 1 pass 158 fail 1
|
||||
Mc-engine-keys (packages/conversation/src/controller.mjs): conversation rc 1 pass 157 fail 2
|
||||
Md-env-inherit (packages/conversation/src/pi-pin.mjs): conversation rc 1 pass 157 fail 2
|
||||
Me-escalating (packages/conversation/src/controller.mjs): conversation rc 1 pass 158 fail 1
|
||||
Mf-queue-catch (packages/conversation/src/terminal.mjs): conversation rc 1 pass 158 fail 1
|
||||
Mg-no-hold (packages/conversation/src/terminal.mjs): conversation rc 1 pass 157 fail 2
|
||||
Mh-manager-env (packages/conversation/src/cohort.mjs): conversation rc 1 pass 158 fail 1
|
||||
Mi-bus-post (packages/webui/src/serve.mjs): webui rc 1 pass 19 fail 1
|
||||
Mj-subject (packages/webui/src/serve.mjs): webui rc 1 pass 19 fail 1
|
||||
Mk-copy-safe (packages/webui/src/public/bus.js): webui rc 0 pass 20 fail 0
|
||||
Ml-option-inert (packages/webui/src/public/bus.js): webui rc 1 pass 19 fail 1
|
||||
Mm-timeout-kill (packages/webui/src/bus.mjs): webui rc 0 pass 20 fail 0
|
||||
Mn-max-bytes (packages/webui/src/bus.mjs): webui rc 1 pass 19 fail 1
|
||||
Mo-status (packages/webui/src/bus.mjs): webui rc 1 pass 18 fail 2
|
||||
Mp-business-id (packages/webui/src/cli.mjs): webui rc 0 pass 20 fail 0
|
||||
DONE
|
||||
@@ -0,0 +1,132 @@
|
||||
✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (11.766822ms)
|
||||
✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.449002ms)
|
||||
✔ completed smoke replies and ordinary questions are idle, not human blockers (0.828459ms)
|
||||
✔ only an explicit first-line input request makes a finished reply waiting (0.202427ms)
|
||||
✔ tool activity, user text, errors and unfinished turns override attention text (0.133911ms)
|
||||
✔ STOP access failure is unknown, not absence, under a non-root identity (32.685194ms)
|
||||
✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (2.670289ms)
|
||||
✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.280195ms)
|
||||
✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (6.060715ms)
|
||||
✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.528774ms)
|
||||
✔ server rescans connector discovery and refuses HTTP reply without transport (34.353982ms)
|
||||
✔ connector session links and linked directories are not read (1.053549ms)
|
||||
✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (2.540764ms)
|
||||
✔ CLI print uses the relaunch notice instead of old current preview (61.017629ms)
|
||||
✔ connector owner and fixed task never inherit a native relaunch notice (1.847241ms)
|
||||
✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.006472ms)
|
||||
✔ loadConfig: missing file throws ConfigError (1.439638ms)
|
||||
✔ loadConfig: invalid JSON throws ConfigError (0.321928ms)
|
||||
✔ loadConfig: missing dataRoot throws ConfigError (0.225256ms)
|
||||
✔ loadConfig: relative dataRoot throws ConfigError (0.220504ms)
|
||||
✔ loadConfig: valid config returns dataRoot (0.673442ms)
|
||||
✔ findNewestSession: picks the newest by mtime among two files (0.484012ms)
|
||||
✔ findNewestSession: finds files in nested subdirectories (0.308315ms)
|
||||
✔ findNewestSession: returns null for a missing dir (0.138032ms)
|
||||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.794291ms)
|
||||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.325106ms)
|
||||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.522182ms)
|
||||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.102793ms)
|
||||
✔ deriveState: full state table (0.169978ms)
|
||||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.375548ms)
|
||||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.337094ms)
|
||||
✔ rule: a finished ordinary turn is idle, even if it says your move (0.301326ms)
|
||||
✔ task: the first user message of the session, from text blocks (0.294023ms)
|
||||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.295016ms)
|
||||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.381194ms)
|
||||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.431789ms)
|
||||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.539949ms)
|
||||
✔ scan: the written record carries task, workspace and activeProject (0.523678ms)
|
||||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.703112ms)
|
||||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.441665ms)
|
||||
✔ registration: a record whose pid is gone is stale; derived values win, sources say derived, registered stays with alive false; a pid the probe cannot decide is not stale; pidAlive itself (1.15204ms)
|
||||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.465846ms)
|
||||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.191089ms)
|
||||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.775236ms)
|
||||
✔ matchRegistration: matches by sessionsDir, and by realpath through a symlink; sessionsDir null never matches; same seat name with a different sessionsDir does not match (fleet vs repo darkwing) (0.356809ms)
|
||||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (1.16833ms)
|
||||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.263286ms)
|
||||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.551571ms)
|
||||
✔ scanAgent: ageSeconds is computed from the injected now (0.286943ms)
|
||||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.192608ms)
|
||||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.374537ms)
|
||||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.37833ms)
|
||||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.95233ms)
|
||||
✔ scan: relative boardDir throws ConfigError (0.086953ms)
|
||||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (57.489098ms)
|
||||
✔ CLI: missing config exits 2 with a refused: message (50.110867ms)
|
||||
✔ CLI: unknown command exits 2 (49.880162ms)
|
||||
✔ CLI: unknown --liveness value exits 2 (49.007096ms)
|
||||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.216076ms)
|
||||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.094031ms)
|
||||
✔ tmuxIsAlive: a pane running pi is alive (0.17491ms)
|
||||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.072056ms)
|
||||
✔ tmuxIsAlive: no such tmux session is not alive (0.04995ms)
|
||||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.059709ms)
|
||||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.091196ms)
|
||||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.075129ms)
|
||||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.070568ms)
|
||||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.087198ms)
|
||||
✔ loadSeen: missing file returns {} (0.17823ms)
|
||||
✔ loadSeen: invalid JSON throws ConfigError (0.170346ms)
|
||||
✔ loadSeen: a JSON array throws ConfigError (0.150123ms)
|
||||
✔ loadSeen: a non-string value throws ConfigError (0.182105ms)
|
||||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.304833ms)
|
||||
✔ markSeen: seen false deletes the key (0.253293ms)
|
||||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.207156ms)
|
||||
✔ markSeen: project containing '/' throws ConfigError (0.114109ms)
|
||||
✔ markSeen: non-boolean seen throws ConfigError (0.103073ms)
|
||||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.277892ms)
|
||||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.223624ms)
|
||||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.213001ms)
|
||||
✔ scanAgent: an error-state session with a matching mark is seen (0.226756ms)
|
||||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.484707ms)
|
||||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.183023ms)
|
||||
✔ taskSetBy: a registered task carries the record's setter; a record without the field (pre-#1511) reads unknown; the value is not copied into registered (0.620208ms)
|
||||
✔ taskSetBy: null whenever the task shown is not the registered one: no registration, an empty registered task, a stale registration; the field is always present (0.558063ms)
|
||||
✔ taskSetBy: scan() reads the field from disk through the seat package (bounded there), writes it to the per-agent record and index, and an invalid on-disk value is a registrationError, never a row value (0.923967ms)
|
||||
✔ isLoopbackHost: recognizes loopback hosts (1.171912ms)
|
||||
✔ isLoopbackHost: rejects non-loopback hosts (4.533566ms)
|
||||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.534117ms)
|
||||
✔ startServer: serves page, healthz, and a rescanning /api/board (33.621069ms)
|
||||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (5.65756ms)
|
||||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (2.235975ms)
|
||||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (50.191509ms)
|
||||
✔ CLI: serve rejects a non-numeric --port with exit 2 (48.933961ms)
|
||||
✔ CLI: scan still works after the async cli refactor (46.774006ms)
|
||||
✔ CLI: live serve prints its URL and answers /healthz (72.151216ms)
|
||||
✔ page.html: esc() escapes every HTML-significant character (1.320292ms)
|
||||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (17.358298ms)
|
||||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (4.982676ms)
|
||||
✔ POST /api/seen with invalid JSON returns 400 (2.463228ms)
|
||||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (3.043078ms)
|
||||
✔ POST /api/seen with a missing agent returns 400 (2.670007ms)
|
||||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (2.513603ms)
|
||||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (65.117838ms)
|
||||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.344723ms)
|
||||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.279548ms)
|
||||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.164434ms)
|
||||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.193962ms)
|
||||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.31428ms)
|
||||
✔ page.html: task and active project cells show their source via sourceTag(); the detail has a Registered row via registeredText(); SOURCE_LABEL maps registration to registered; every dynamic value in sourceTag/fromSource/registeredText is escaped (0.600716ms)
|
||||
✔ POST /api/reply: runs agent-send.sh with -s from the registration, -S <host>:control-board, -m text plus the fixed trailer, no -L on the default socket, MOSAIC_TMUX_SOCKET stripped; answers delivered with the exit code and both streams (31.701551ms)
|
||||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (33.178205ms)
|
||||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (29.032398ms)
|
||||
✔ POST /api/reply: refusals before the tool runs: empty or blank or long text 400, unknown row 404, no registration 409, stale registration 409, no tmux session 409, bad JSON 400; the tool is never called (14.509987ms)
|
||||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.553737ms)
|
||||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.157795ms)
|
||||
✔ page.html: the reply box appears only where canReply() holds (live registration with a tmux session), the detail has a Reply row, the submit posts JSON to /api/reply, receipts and drafts survive a refresh, and every receipt value is escaped (1.17309ms)
|
||||
✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.615908ms)
|
||||
✔ page.html: the task cell and detail show who set a registered task via setByTag()/setByText(), both escaped, only from rec.taskSetBy; the reply gate does not read it (0.294448ms)
|
||||
✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (8.579537ms)
|
||||
✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (33.724672ms)
|
||||
✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (5.455968ms)
|
||||
✔ every refusal code the reader can raise has an HTTP status (0.921898ms)
|
||||
✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (47.081144ms)
|
||||
ℹ tests 124
|
||||
ℹ suites 0
|
||||
ℹ pass 124
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 682.030237
|
||||
@@ -0,0 +1,167 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (210.726364ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (34.368141ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (20.018785ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (380.116232ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (456.060011ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (252.098137ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (206.94115ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (187.991244ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.341587ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10487.08231ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (30391.183101ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (255.457824ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (150.488448ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (386.197697ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (272.276158ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (345.385932ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (105.058156ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (166.619923ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (252.611695ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (740.641729ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (187.106016ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (90.148273ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (76.106326ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.392358ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.227629ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.737675ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2974.632634ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (433.906647ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2772.275713ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5113.127761ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2539.623106ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2560.679576ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2428.224863ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5090.323954ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (794.901897ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (760.551247ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (569.400905ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (822.500321ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (786.660593ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (1027.13246ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3314.913825ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (30.623643ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (695.708044ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (619.768071ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.105052ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (210.228801ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (203.250995ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (201.506341ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (365.393559ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (173.007345ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.626767ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (215.491813ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (313.157374ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (212.947715ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (186.839278ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (206.56574ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (206.280951ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.72526ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (211.076426ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (219.018188ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (243.642242ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (217.491619ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.433314ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.267288ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.307547ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.472234ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.570073ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.417176ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.106956ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (271.901516ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (251.43548ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (291.514657ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (891.996436ms)
|
||||
✔ H4: self-takeover is refused (183.681359ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (423.972439ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1042.755354ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (241.650368ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (713.711676ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (281.441201ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (202.4407ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (204.470699ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (589.915885ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (304.148401ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (217.646302ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1111.126289ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (367.956082ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1118.722686ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (315.641387ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (501.842991ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.654734ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (670.364645ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (565.368884ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (804.860882ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2924.807529ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (647.650662ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.978617ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.740327ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.639434ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.479043ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.582358ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.873284ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.778796ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (2.149743ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.111216ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.490035ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.288928ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.055357ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.392195ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.138011ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.759151ms)
|
||||
✔ F9: registrations never add or redirect a root (1.35958ms)
|
||||
✔ F10: a header cwd naming another project is refused (2.853238ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.68484ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.959658ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.092588ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.204255ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.68482ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (554.100134ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.303574ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.553846ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.459406ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.147763ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.986648ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (802.071262ms)
|
||||
✔ the engine pin holds for the installed package (1.948631ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (266.986593ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (575.722151ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (254.170702ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (490.701233ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (513.244073ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (207.007401ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.083634ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (377.539865ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (188.745974ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (294.322787ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (471.836066ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1873.555156ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (201.858805ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (296.58971ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (193.331941ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (203.021271ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (374.483019ms)
|
||||
✔ N10: fake conformance (32.042935ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (451.519119ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (304.56146ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (486.046883ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (369.30683ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (492.532654ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (533.463148ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (330.30135ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (608.441051ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (668.203481ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (575.61075ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (535.682213ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (216.918194ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (204.71571ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (214.452163ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (237.014139ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (225.951441ms)
|
||||
ℹ tests 159
|
||||
ℹ suites 0
|
||||
ℹ pass 159
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 47611.360851
|
||||
@@ -0,0 +1,29 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4036.351556ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4466.950246ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5115.863809ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (276.002468ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (164.156539ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (142.726233ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2558.479321ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (182.557938ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3892.459033ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2071.286884ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1118.968483ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (801.668669ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4439.861056ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54030.58281ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4574.368627ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21722.491531ms)
|
||||
✔ loopback host and board origin fail closed (8.669555ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (111.97983ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (95.364647ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1521.573248ms)
|
||||
ℹ tests 20
|
||||
ℹ suites 0
|
||||
ℹ pass 20
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54445.987031
|
||||
@@ -0,0 +1,6 @@
|
||||
separate chunks: sent []; composer ""; status "takeover: transferred"
|
||||
order: interrupt start -> interrupt end -> takeover start -> takeover end
|
||||
one chunk after Ctrl-G: sent []; composer ""; status "takeover: transferred"
|
||||
order: interrupt start -> interrupt end -> takeover start -> takeover end
|
||||
control: no earlier action: sent ["hi"]; composer ""; status "prompt: admitted"
|
||||
order: takeover start -> takeover end -> prompt hi
|
||||
@@ -0,0 +1,5 @@
|
||||
Mm: humanCall -> outcome-unknown after 706 ms; hung CLI pid alive after: false
|
||||
Mp: --business "../acme" -> exit 2: refused: business must be a business id
|
||||
Mp: --business "-x" -> exit 2: refused: business must be a business id
|
||||
Mp: --business "acme corp" -> exit 2: refused: business must be a business id
|
||||
Mp: --business "acme" -> exit 2: refused: port must be 0..65535
|
||||
@@ -0,0 +1,13 @@
|
||||
node-webui rc=0 ℹ pass 20 ℹ fail 0 54s
|
||||
node-conversation rc=0 ℹ pass 159 ℹ fail 0 48s
|
||||
node-control-board rc=0 ℹ pass 124 ℹ fail 0 0s
|
||||
test-auth rc=0 OK auth.sh without valid config refuses selftest: 15 passed, 0 failed 1s
|
||||
test-conductor rc=0 OK invalid policy exits 2 (exit 2) selftest: 17 passed, 0 failed 5s
|
||||
test-config rc=0 OK failed validation modified nothing selftest: 24 passed, 0 failed 2s
|
||||
test-discord rc=0 OK service install with USER unset finishes and names the account for lingering discord suite: 66 passed, 0 failed 7s
|
||||
test-extension-package rc=0 OK nested second entrypoint fails closed extension package selftest: 18 passed, 0 failed 1s
|
||||
test-foundation rc=0 OK no field of the non-bundle file is echoed selftest: 44 passed, 0 failed 38s
|
||||
test-queue rc=0 skip queue verify and render --check: this checkout (/home/jwoltje/darkwing-scratch/r40/wt) is not the queue's canonical root (/mnt/storage/src/mosaic-stack) 33s
|
||||
test-release rc=0 skip state-machine cases (docker daemon unavailable) selftest: 4 passed, 0 failed 0s
|
||||
test-task rc=1 OK no agent identity on headless run selftest: 26 passed, 2 failed 2s
|
||||
DONE
|
||||
@@ -0,0 +1,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,55 @@
|
||||
Note: switching to 'f19787ee609cd303f9b4c5a15fc9cac52a50cb4e'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
Not currently on any branch.
|
||||
nothing to commit, working tree clean
|
||||
Note: switching to 'f19787ee609cd303f9b4c5a15fc9cac52a50cb4e'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,26 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 24 passed, 0 failed
|
||||
@@ -0,0 +1,70 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
|
||||
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
|
||||
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
|
||||
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
|
||||
OK real pi with protocol vault adds reserve_id to the git verbs
|
||||
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
|
||||
OK real pi refuses a git key on a read-only root (fail closed)
|
||||
OK real pi with the pilot flags (--no-tools) exposes no tool at all
|
||||
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 178)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
|
||||
discord suite: 66 passed, 0 failed
|
||||
@@ -0,0 +1,21 @@
|
||||
OK initial ordinary-file install
|
||||
OK installed tree matches canonical source
|
||||
OK installed tree has no symlinks
|
||||
OK check detects installation drift
|
||||
OK sync refuses to overwrite installation drift
|
||||
OK check detects an extra destination file
|
||||
OK check detects an extra destination directory
|
||||
OK check rejects a destination symlink
|
||||
OK sync accepts a canonical source update
|
||||
OK updated installation matches canonical source
|
||||
scripts/test-extension-package.sh: line 14: 546752 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
OK sync succeeds after interruption recovery
|
||||
OK unlocked stale lock file does not block
|
||||
OK active lock refuses a concurrent sync
|
||||
OK source symlink fails closed
|
||||
OK nested second entrypoint fails closed
|
||||
|
||||
extension package selftest: 18 passed, 0 failed
|
||||
@@ -0,0 +1,53 @@
|
||||
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
|
||||
|
||||
OK syntax: scripts/foundation-inspect.mjs
|
||||
OK syntax: scripts/foundation/strict-json.mjs
|
||||
OK syntax: scripts/foundation/canonical.mjs
|
||||
OK syntax: scripts/foundation/resolve.mjs
|
||||
OK syntax: scripts/foundation/validate-record.mjs
|
||||
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
|
||||
OK syntax: scripts/foundation/canonical.test.mjs
|
||||
OK syntax: scripts/foundation/cli.test.mjs
|
||||
OK syntax: scripts/foundation/fixtures.test.mjs
|
||||
OK syntax: scripts/foundation/resolve.test.mjs
|
||||
OK syntax: scripts/foundation/strict-json.test.mjs
|
||||
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
|
||||
OK fixture generator runs
|
||||
OK checked-in fixtures/bundles equal a fresh generation
|
||||
OK checked-in fixtures/raw equal a fresh generation
|
||||
OK checked-in fixtures/index.json equal a fresh generation
|
||||
OK checked-in demo bundles equal a fresh generation
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test scripts/foundation/ (ℹ pass 80)
|
||||
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
|
||||
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
|
||||
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
|
||||
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
|
||||
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
|
||||
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
|
||||
OK oracle: zero schema-column disagreements with the pinned checker
|
||||
OK oracle: strict-only profile refusals are counted and asserted
|
||||
OK demo: permitted read preview exits 0 (exit 0)
|
||||
OK demo: permitted file.change preview exits 0 (exit 0)
|
||||
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
|
||||
OK demo: revoked registration is refused (exit 3) (exit 3)
|
||||
OK demo: message is not authority (exit 3) (exit 3)
|
||||
OK usage: no arguments exits 2 (exit 2)
|
||||
OK io: missing file exits 4 (exit 4)
|
||||
OK io: directory exits 4 (exit 4)
|
||||
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
|
||||
OK bound: oversize fixture exits 2 (exit 2)
|
||||
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
|
||||
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
|
||||
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
|
||||
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
|
||||
OK text output starts with the disclaimer
|
||||
OK json output is valid JSON with result allowed and exactly the charter §7 fields
|
||||
OK json golden matches byte-for-byte
|
||||
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
|
||||
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
|
||||
OK canary never printed (bundle run and credential-file run)
|
||||
OK a non-bundle JSON file is refused at the shape gate, not read into output
|
||||
OK no field of the non-bundle file is echoed
|
||||
|
||||
selftest: 44 passed, 0 failed
|
||||
@@ -0,0 +1,35 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 148
|
||||
ℹ pass 148
|
||||
ℹ fail 0
|
||||
OK node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/darkwing-scratch/r40/wt) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
@@ -0,0 +1,16 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
OK status safe on empty state (exit 0)
|
||||
OK status created no pointer
|
||||
OK fault-injected activation refuses (exit 1)
|
||||
OK refused activation wrote no pointer
|
||||
OK refusal logged exactly once with valid fields
|
||||
OK healthy activation succeeds (exit 0)
|
||||
OK pointer written with valid fields
|
||||
OK repeat activation succeeds (log grows) (exit 0)
|
||||
OK log is append-only across activations
|
||||
OK rollback without previous refuses (exit 1)
|
||||
|
||||
selftest: 14 passed, 0 failed
|
||||
@@ -0,0 +1,7 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
skip state-machine cases (docker daemon unavailable)
|
||||
|
||||
selftest: 4 passed, 0 failed
|
||||
@@ -0,0 +1,33 @@
|
||||
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
|
||||
FAIL user recall run succeeds (exit 1)
|
||||
FAIL recalled user name (response: )
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 26 passed, 2 failed
|
||||
@@ -0,0 +1,35 @@
|
||||
// Does input held behind a Ctrl-T wait for that takeover when an earlier
|
||||
// chunk's action is still running? An observer presses Ctrl-G (interrupt,
|
||||
// slow), then Ctrl-T and "hi" Enter. One key at a time, the takeover runs
|
||||
// before the Enter is judged, so "hi" is sent. Imports from the review
|
||||
// worktree by absolute path.
|
||||
const WT = process.env.WT;
|
||||
const { Terminal } = await import(`${WT}/packages/conversation/src/terminal.mjs`);
|
||||
function stub() {
|
||||
const sent = [], log = [];
|
||||
let listener = () => {}, openInt, openTk;
|
||||
const intGate = new Promise((r) => (openInt = r)), tkGate = new Promise((r) => (openTk = r));
|
||||
const s = {
|
||||
closed: false, connection: { id: "conn-1" }, binding: { state: "active", controllerConnection: "conn-2" }, isController: false,
|
||||
on: (fn) => (listener = fn),
|
||||
observe: async () => ({ outcome: "refused:channel", refusal: "channel" }),
|
||||
prompt: async (t) => (log.push(`prompt ${t}`), sent.push(t), { outcome: "admitted", refusal: null, receipt: { id: "r1", state: "admitted" } }),
|
||||
interrupt: async () => { log.push("interrupt start"); await intGate; log.push("interrupt end"); return { outcome: "refused:controller", refusal: "controller" }; },
|
||||
takeover: async () => {
|
||||
log.push("takeover start"); await tkGate;
|
||||
s.isController = true; s.binding = { state: "active", controllerConnection: "conn-1" }; listener({ type: "push", kind: "binding" });
|
||||
log.push("takeover end"); return { outcome: "transferred", refusal: null };
|
||||
},
|
||||
};
|
||||
return { s, sent, log, openInt, openTk };
|
||||
}
|
||||
for (const [name, chunks] of [["separate chunks", ["\x07", "\x14", "hi\r"]], ["one chunk after Ctrl-G", ["\x07", "\x14hi\r"]], ["control: no earlier action", ["\x14", "hi\r"]]]) {
|
||||
const { s, sent, log, openInt, openTk } = stub();
|
||||
const term = new Terminal({ client: s });
|
||||
const done = chunks.map((c) => term.key(c).catch((e) => log.push(`threw ${e.message}`)));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
openInt(); await new Promise((r) => setTimeout(r, 10));
|
||||
openTk(); await Promise.all(done);
|
||||
console.log(`${name}: sent ${JSON.stringify(sent)}; composer ${JSON.stringify(term.composer)}; status ${JSON.stringify(term.status)}`);
|
||||
console.log(` order: ${log.join(" -> ")}`);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Two of the surviving mutants, probed against the candidate. Mm: does
|
||||
// humanCall kill a human CLI that never answers once the wait runs out? Mp:
|
||||
// does `serve --business` refuse a value that isn't a business id? Imports
|
||||
// from the review worktree by absolute path (WT). The bad --port after
|
||||
// --business stops the CLI before it would start a server.
|
||||
import { mkdtempSync, readFileSync, writeFileSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { spawnSync } from "node:child_process";
|
||||
const WT = process.env.WT;
|
||||
const { humanCall } = await import(`${WT}/packages/webui/src/bus.mjs`);
|
||||
const root = mkdtempSync(join(tmpdir(), "r40-surv-"));
|
||||
const cli = join(root, "hang.mjs"), pidFile = join(root, "pid");
|
||||
writeFileSync(cli, `require("fs").writeFileSync(${JSON.stringify(pidFile)}, String(process.pid)); setInterval(() => {}, 1000);\n`.replace("require(\"fs\")", "(await import('node:fs'))"));
|
||||
const t0 = Date.now();
|
||||
const err = await humanCall({ socket: "s", business: "demo", cli, timeoutMs: 500 })("inbox").catch((e) => e);
|
||||
const pid = Number(readFileSync(pidFile, "utf8"));
|
||||
await new Promise((r) => setTimeout(r, 200));
|
||||
let alive = true; try { process.kill(pid, 0); } catch { alive = false; }
|
||||
console.log(`Mm: humanCall -> ${err.code} after ${Date.now() - t0} ms; hung CLI pid alive after: ${alive}`);
|
||||
if (alive) process.kill(pid, "SIGKILL");
|
||||
for (const value of ["../acme", "-x", "acme corp", "acme"]) {
|
||||
const r = spawnSync(process.execPath, [`${WT}/packages/webui/src/cli.mjs`, "serve", "--business", value, "--port", "99999"], { encoding: "utf8", env: { PATH: process.env.PATH, HOME: root } });
|
||||
console.log(`Mp: --business ${JSON.stringify(value)} -> exit ${r.status}: ${r.stderr.trim()}`);
|
||||
}
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
@@ -0,0 +1,204 @@
|
||||
# Row 40, slice 1 S5 (Console bus views and conversation hardening), round 2 review (Darkwing)
|
||||
|
||||
Issue #1522, request comment 26996, notes comment 26997, queue rev 249.
|
||||
Candidate: 27 files, uncommitted in the main checkout, listed in
|
||||
`files.txt`. Manifest `agents/dewey/work/queue-40/candidate-manifest-r2.sha256`
|
||||
(copy here), sha256
|
||||
`5cbf22c0b25914802cd574ae53fc02aaf0a5bec731f45735f867474372100623`.
|
||||
Round 1 review: Filbert, comment 26993.
|
||||
|
||||
Verdict: **changes**, comment 27001. One finding, T1. The terminal's input hold, which
|
||||
answers Filbert F2 from #1507, fails when an earlier chunk's action is
|
||||
still running. A Ctrl-T typed then drops the text behind it with no
|
||||
message, so the F2 property the header and DEFERRED claim is false in
|
||||
that case. Nothing wrong is ever sent. The rest of round 2 holds under my
|
||||
reading, probes and mutants: the README's Console-process boundary,
|
||||
`--no-approve` in SEAL_FLAGS, the queue surviving a throw, and the bus
|
||||
read path.
|
||||
|
||||
## Method
|
||||
|
||||
- A detached worktree at `f19787ee`. I extracted the 27 candidate files
|
||||
over it and ran `sha256sum -c` on the manifest: 27 OK. I symlinked
|
||||
`node_modules` and ran `git add -N .`. After the probes and mutants I
|
||||
checked again: 27 OK (`mut/manifest-after.txt`).
|
||||
- I read `public/bus.js` in full, plus `serve.mjs`, `bus.mjs`, `cli.mjs`,
|
||||
the conversation diffs (`pi-pin.mjs`, `controller.mjs`, `cohort.mjs`,
|
||||
`terminal.mjs`), the new tests, both READMEs, the DEFERRED changes,
|
||||
`S5-GATE-E.md` and Dewey's mutant evidence. I checked Filbert's comment
|
||||
26993 against each change. For `--no-approve` I read the pinned Pi
|
||||
0.85.1's `main.js` (the trust override) and the resource loader.
|
||||
- Probes in `probe/` import from my scratch worktree by absolute path
|
||||
(`WT`), so they won't run as committed without setting it.
|
||||
- Sixteen mutants of my own (`mut/mutate.py`, `mut/run.sh`). Each ran the
|
||||
node suite of its package, then the file was restored from a backup
|
||||
copy, since the candidate isn't committed in the worktree.
|
||||
|
||||
Node is v26.8.1, `TMPDIR=~/darkwing-scratch/tmp`. `gate.sh` set
|
||||
`DOCKER_HOST=unix:///nonexistent.sock`, which matters for two suites
|
||||
below.
|
||||
|
||||
## Suites
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| webui | 20/20 |
|
||||
| conversation | 159/159 |
|
||||
| control-board | 124/124 |
|
||||
| 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 | skipped: the worktree isn't the queue's canonical root |
|
||||
| test-release | 4/0 with Docker blocked; 14/0 run alone with Docker (`out/test-release-rerun.txt`) |
|
||||
| test-task | 26/2: "user recall run succeeds" and "recalled user name" |
|
||||
|
||||
The two test-task failures are a live recall: `run-task.sh` runs a real
|
||||
Pi worker in Docker against the zai provider. With Docker blocked they
|
||||
can't run, and with Docker they'd be a paid model call, which I didn't
|
||||
make. They say nothing about this candidate, but I can't confirm Dewey's
|
||||
"every test-*.sh green" for them.
|
||||
|
||||
## T1: input held behind a Ctrl-T is drained by an earlier chunk's run (blocking)
|
||||
|
||||
`packages/conversation/src/terminal.mjs:102-140, 194-195`. `#parse` puts
|
||||
the rest of a chunk after Ctrl-T or Ctrl-O into `this.held`. `#run`
|
||||
drains `this.held` after its own actions finish, whichever chunk set it.
|
||||
When chunk 1's action is still running (a slow Ctrl-G interrupt), chunk 2
|
||||
with Ctrl-T sets `held` at parse time. Chunk 1's `#run` then finishes,
|
||||
finds `held`, and parses and runs it before chunk 2's takeover has started.
|
||||
The Enter is judged as an observer and refused (`#take`, line 236), which
|
||||
leaves the text in the composer. The takeover then clears the composer on
|
||||
the control transfer (line 83), so the text is gone, and its status
|
||||
replaces the refusal: the line shows only "takeover: transferred".
|
||||
|
||||
`probe/hold-order.mjs`, an observer pressing Ctrl-G, Ctrl-T, then "hi"
|
||||
and Enter (`out/probe-hold-order.txt`):
|
||||
|
||||
```
|
||||
separate chunks: sent []; composer ""; status "takeover: transferred"
|
||||
order: interrupt start -> interrupt end -> takeover start -> takeover end
|
||||
one chunk after Ctrl-G: sent []; composer ""; status "takeover: transferred"
|
||||
order: interrupt start -> interrupt end -> takeover start -> takeover end
|
||||
control: no earlier action: sent ["hi"]; composer ""; status "prompt: admitted"
|
||||
order: takeover start -> takeover end -> prompt hi
|
||||
```
|
||||
|
||||
The order line doesn't show the refused Enter, because the stub's
|
||||
`prompt` is never reached. It is refused inside the terminal before that.
|
||||
|
||||
The header (`terminal.mjs:18`) says input after Ctrl-T or Ctrl-O "waits
|
||||
until that finishes, in the same chunk or a later one", and DEFERRED moves
|
||||
Filbert F2 to done. Both are true only when no earlier action is pending.
|
||||
Pressing Ctrl-G and then Ctrl-T to take control is the natural sequence
|
||||
for an observer who wants to stop a turn, so this isn't a contrived order.
|
||||
|
||||
Fix: drain `held` only in the run whose chunk set it. For example,
|
||||
`key()` records whether its own `#parse` set `held` and passes that flag
|
||||
to `#run`, and `#run` recomputes it after each `#parse` of the drained
|
||||
text. Add a test with the probe's shape: an earlier action that's still
|
||||
pending, then Ctrl-T and Enter, both as separate chunks and as one.
|
||||
|
||||
## What holds
|
||||
|
||||
- **Console-process boundary (Filbert F1).** The WebUI README now says the
|
||||
bus's ancestry proof ends at the Console process. Anything that can
|
||||
connect to port 7330 gets reader-level reads, including T3 seats and S6
|
||||
sessions on the same UID. Reads only. That matches the code: `serve.mjs`
|
||||
checks Host (loopback plus port) and Origin before every route, the bus
|
||||
routes are GET-only (405 otherwise, HEAD included), and `busRead` checks
|
||||
the trail subject before it asks the bus.
|
||||
- **`--no-approve` in SEAL_FLAGS.** In Pi 0.85.1 `main.js:570-581`,
|
||||
`--no-approve` makes `projectTrusted` false whatever `trust.json` says.
|
||||
That gates the project's SYSTEM.md, APPEND_SYSTEM.md (resource loader
|
||||
809-830), settings and skills. The new real-Pi smoke test checks the
|
||||
sealed case against a trusted project, its `--approve` control offers
|
||||
`skill:probe`, and `checkSeal` refuses `--approve` and `-a`.
|
||||
- **Engine seal.** `controller.mjs` accepts only `{extraArgs, cwd,
|
||||
envKeys}` (`UNSEALED_ENGINE` otherwise, line 171), requires
|
||||
`process.execPath` with the pinned Pi entry when sealed, and checks at
|
||||
construction and at bind. `engineEnv` builds the environment from
|
||||
ENGINE_ENV plus envKeys names that match the credential pattern.
|
||||
- **Queue after a throw (Filbert N1).** `key()` chains on
|
||||
`this.queue.catch(() => {})`, and `input()` puts the error in the status
|
||||
line. Both are covered.
|
||||
- **Force-stop escalation.** The catch around the fence clears
|
||||
`escalating` (line 712), covered by the races test.
|
||||
- **Bus read path.** `humanCall` kills the human CLI on timeout and on
|
||||
`maxBytes`, maps bus error codes to statuses (403 for refusals of this
|
||||
caller, 503 for no answer, 502 otherwise), and never returns a path or a
|
||||
stack. `public/bus.js` routes every bus string through `esc`, `inert`
|
||||
or `txt`. The copy command is offered only for an open, human-routed
|
||||
decision whose id and key both pass SAFE, the broker's own identifier
|
||||
rule, so no shell metacharacter or leading dash can reach it.
|
||||
|
||||
## Mutants
|
||||
|
||||
| Mutant | Change | Result | Killed by |
|
||||
|---|---|---|---|
|
||||
| Ma-no-approve | `--no-approve` removed from SEAL_FLAGS | killed | smoke: sealed Pi ignores a trusted project's .pi resources |
|
||||
| Mb-seal-command | the sealed command check skipped | killed | N24b |
|
||||
| Mc-engine-keys | an unknown engine key accepted | killed | N24b, N24 |
|
||||
| Md-env-inherit | `engineEnv` copies the whole source environment | killed | K19, N24b |
|
||||
| Me-escalating | the fence's catch keeps `escalating` | killed | races: a force stop whose fence throws |
|
||||
| Mf-queue-catch | `key()` chains without catching | killed | terminal: after an action throws (N1) |
|
||||
| Mg-no-hold | no action holds input | killed | terminal: F2, N1 |
|
||||
| Mh-manager-env | the scope gets the engine env without MANAGER_ENV | killed | K19 |
|
||||
| Mi-bus-post | a non-GET bus request is served | killed | bus routes are GET-only |
|
||||
| Mj-subject | the trail subject check skipped | killed | bus routes serve the four reads |
|
||||
| Mk-copy-safe | the copy command offered without SAFE | **survived** | |
|
||||
| Ml-option-inert | option text rendered raw | killed | S5 pages in a browser |
|
||||
| Mm-timeout-kill | no SIGKILL when the wait runs out | **survived** | |
|
||||
| Mn-max-bytes | no `maxBytes` limit | killed | humanCall speaks the human transport protocol |
|
||||
| Mo-status | `human-required` maps to 502 | killed | bus refusals map to statuses; S5 pages |
|
||||
| Mp-business-id | `serve --business` takes any value | **survived** | |
|
||||
|
||||
Thirteen of 16 killed. For the three survivors I checked the code by hand:
|
||||
|
||||
- Mk can't be reached through a real broker. `broker.mjs:542` refuses an
|
||||
option key that fails `id()`, the same rule as SAFE, so the browser
|
||||
check is a second layer, and the browser test seeds a real broker. A test
|
||||
would need a stubbed `views` that returns a bad key.
|
||||
- Mm: `probe/survivors.mjs` runs `humanCall` against a CLI that never
|
||||
answers, with a 500 ms wait: `outcome-unknown after 706 ms; hung CLI pid
|
||||
alive after: false` (the 706 includes a 200 ms pause before the check).
|
||||
- Mp: the same probe runs `serve --business` with `../acme`, `-x` and
|
||||
`acme corp`, and each exits 2 with "business must be a business id".
|
||||
`acme` passes and stops at the bad port given after it, before any
|
||||
server starts.
|
||||
|
||||
The code is right in all three; no test holds Mm or Mp. A `humanCall`
|
||||
test with a hanging CLI and a `cli.mjs` test with a bad `--business` would.
|
||||
|
||||
## Notes (not blocking)
|
||||
|
||||
1. `docs/plans/DEFERRED.md` is Sage's file, and the candidate edits it.
|
||||
The N2 entry is right. The line that moves Filbert F2 to done should
|
||||
wait for T1.
|
||||
2. SEAL_FLAGS leaves context files on. Under the seal, Pi still loads
|
||||
AGENTS.md and CLAUDE.md (and their variants, `resource-loader.js:33`)
|
||||
from the working directory and its parents.
|
||||
They are prompt text, not code, and a human conversation may want them.
|
||||
But Pi has `--no-context-files`, the worker adapter uses it, and neither
|
||||
README says the conversation engine doesn't. One line would settle it.
|
||||
3. The copied command is `mosaic decide <id> <key>` with no `--business`.
|
||||
When the Console runs with `--business X` and the live host serves Y,
|
||||
the command goes to Y, which refuses an unknown decision. That fails
|
||||
closed, so this is a note only.
|
||||
4. The cohort scope still passes `DBUS_SESSION_BUS_ADDRESS` to the engine
|
||||
(`cohort.mjs:106`). The README already says so.
|
||||
5. Gate E (`S5-GATE-E.md`) isn't run yet, and nothing outside the tests
|
||||
constructs a Controller. Expected at this stage; I mention it only so
|
||||
the round 2 approval, when it comes, isn't read as covering it.
|
||||
|
||||
## Files
|
||||
|
||||
- `review-r2.md`: this review.
|
||||
- `files.txt`, `candidate-manifest-r2.sha256`: the candidate as reviewed.
|
||||
- `gate.sh`, `out/`: the suite runs, `out/summary.txt` first.
|
||||
- `probe/hold-order.mjs`, `out/probe-hold-order.txt`: T1.
|
||||
- `probe/survivors.mjs`, `out/probe-survivors.txt`: Mm and Mp.
|
||||
- `mut/`: `mutate.py`, `run.sh`, `all.sh`, `summary.txt`, one output per
|
||||
mutant, and `manifest-after.txt` (27).
|
||||
Reference in New Issue
Block a user