docs(review): row 40 round 3 review packet, approve (darkwing)
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
|
||||
40034b67f4cc4cd007eff36502ae723882688feffa8a5856981950c3218c611c agents/dewey/work/wui/evidence/s5-mutants.md
|
||||
601e39d6f203f969c84dd9c5d23e28b191071c71f2a35374ebea18d2683e243a docs/plans/DEFERRED.md
|
||||
c38191d302da8757a3329abed5e443d06d8eb7b09a0f583d67827ffc1a74e838 packages/conversation/README.md
|
||||
38621d505f6ffd859e37c4309f7a2d3a3ee21a53d1ba41b8ff9ca29250a6dc37 packages/conversation/src/cohort.mjs
|
||||
26f8fa2c7caf794e6224802b8e5d8882419d0bda725d2ab30f0a058a678a57b9 packages/conversation/src/controller.mjs
|
||||
a06468434e7bf3a3dfc3c5ea05b8c21a1510f8fa0c8eaa6e9908b970fa98367e packages/conversation/src/pi-pin.mjs
|
||||
c24d28a073659640b8b027eb12ad150ba895c3001091b892ecef2ec1da40d5cd 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
|
||||
0c2647b62c540bee7164686ea028c5a48e2d570af967a5b3dfa271893a51d6d8 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
|
||||
18ca9e95d8dbda1279fb96d6c38bac1142b6da9990b6cb36499c205882380b58 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
|
||||
2af65f0d48ec7632e12a22aeb5c84ae4c5c183fbb08e282fe557bafc2764b8b5 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/r40c/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,217 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (216.441574ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (31.875599ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.1636ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (398.226853ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (431.148668ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (255.421161ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (203.859835ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (189.380547ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.47328ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10658.422189ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (29094.518667ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (257.986498ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (144.519371ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (419.507196ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (269.990721ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (335.636052ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (101.44507ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (167.830095ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (263.44201ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (735.932032ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (185.197623ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (91.101347ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (72.861514ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (5.068487ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.183086ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.921935ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2582.412882ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (442.793822ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2490.392635ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4735.026818ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2516.965438ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2524.529235ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2423.615707ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5008.606841ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (834.615934ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (590.527352ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (470.106868ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (883.942401ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (600.536854ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (510.777691ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3243.612573ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (20.564288ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (403.358121ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (261.303651ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.132046ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (205.125888ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (237.662439ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (193.256941ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (296.792964ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (197.706648ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.764437ms)
|
||||
✔ 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.028361ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (309.122338ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (204.129171ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (187.55121ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (202.900433ms)
|
||||
✔ 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.205289ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.821827ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (203.098874ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (223.800977ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (243.780629ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (179.554169ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.455622ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.28622ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.341292ms)
|
||||
✔ 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.542432ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (29.771553ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.858419ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.454032ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.409066ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.11515ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (253.046617ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (252.453788ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (324.3337ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (824.383631ms)
|
||||
✔ H4: self-takeover is refused (188.25691ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (434.289017ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1021.1381ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (200.685223ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (568.772484ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (268.480302ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (123.162232ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (210.520329ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (677.993442ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (310.300517ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (188.387822ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (831.32342ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (344.741631ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (925.51757ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (263.993116ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (368.041654ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.824721ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (724.841912ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (657.127416ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (724.045834ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3314.996803ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (644.794325ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.183873ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.942661ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.776179ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.589293ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.997313ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.777885ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.909513ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.305239ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (5.850601ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.288045ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (9.31438ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (8.403081ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (9.132707ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.314702ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.273148ms)
|
||||
✔ F9: registrations never add or redirect a root (1.597532ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.49055ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.784618ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.135685ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.67693ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.298769ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.729814ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (561.19935ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.557575ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.474582ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.22427ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.018767ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.906781ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (898.237037ms)
|
||||
✔ the engine pin holds for the installed package (1.959657ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (291.759948ms)
|
||||
✖ 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) (301.082248ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (284.019352ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (520.523504ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (453.114059ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (226.986306ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.235113ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (357.217428ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (197.658689ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (295.541096ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (452.008968ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1897.868365ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (224.526949ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (330.348176ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (206.099525ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (229.941327ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (369.603ms)
|
||||
✔ N10: fake conformance (33.768124ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (424.810937ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (264.795464ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (281.312756ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (361.68432ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (511.916133ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (456.508258ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (214.434669ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (353.218891ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (787.067711ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (655.375911ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (540.881647ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (245.403854ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (224.145425ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (198.898676ms)
|
||||
✔ 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 (451.889987ms)
|
||||
✔ 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 (190.712834ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 160
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 46482.899678
|
||||
|
||||
✖ 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) (301.082248ms)
|
||||
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/r40c/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,185 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (217.412165ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (31.703231ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.12461ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (505.450925ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (461.25034ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (280.012337ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (225.527967ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (212.530538ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.322044ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9854.398835ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28398.947835ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (256.956387ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (147.663883ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (385.393198ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (271.767391ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (366.218557ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (106.680356ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (168.007025ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (260.512945ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (729.340992ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (184.649191ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (94.819727ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (70.318762ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.897217ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.096525ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.719853ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2607.723662ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (545.250489ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2562.093795ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4749.744555ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2573.562278ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2469.183245ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2445.984678ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4923.046568ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (654.03052ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (567.825644ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (509.381625ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (678.846359ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (357.652954ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (489.623533ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3373.217409ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (41.075074ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (324.22541ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (343.053381ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (81.642916ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (204.554136ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (199.696713ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (296.202561ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (373.234755ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (252.69287ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.467903ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (234.47642ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (317.483573ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (222.941472ms)
|
||||
✔ 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.138251ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (214.663169ms)
|
||||
✔ 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 (238.356521ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.98768ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (294.378718ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (251.865348ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (258.085991ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (201.06573ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.402653ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.278646ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.317569ms)
|
||||
✔ 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.470947ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.503922ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.802151ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.39944ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.384215ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.119746ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (219.850884ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (253.113848ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (334.34807ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1009.888902ms)
|
||||
✔ H4: self-takeover is refused (212.651704ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (459.24516ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1245.171331ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (211.079317ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (566.654958ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (291.178611ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (181.967842ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (188.861053ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (549.356516ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (287.426249ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (200.444487ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (857.504193ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (373.700939ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (887.924023ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (237.241452ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (478.20367ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.624939ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (686.653321ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (532.060281ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (775.209066ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2925.165901ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (562.491533ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.270405ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.609092ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.753356ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.527188ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.431048ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.071325ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.827319ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.305484ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.904469ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.838439ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.06037ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (6.251335ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.983893ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.487498ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.66451ms)
|
||||
✔ F9: registrations never add or redirect a root (1.687486ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.387969ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (1.074852ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (6.082696ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.794846ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.25014ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.676562ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (548.131627ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.69177ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.467359ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.238703ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.04245ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.880023ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (793.400268ms)
|
||||
✔ the engine pin holds for the installed package (1.831292ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (271.760635ms)
|
||||
✔ 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) (542.750506ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (252.66582ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (475.738758ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (617.59465ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (291.571227ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.144547ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (392.153943ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (207.011ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (305.791264ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (569.435704ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1938.11055ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (191.350582ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (274.957818ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (180.110451ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (203.005388ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (397.085912ms)
|
||||
✔ N10: fake conformance (32.298405ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (425.442611ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (275.621285ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (284.445041ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (392.028686ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (360.535077ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (529.453942ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (322.859168ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (418.962901ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (695.398228ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (477.457834ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (611.262487ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (253.000269ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (155.251972ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (196.68809ms)
|
||||
✔ 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 (243.109589ms)
|
||||
✖ 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.55856ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 160
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 45166.946228
|
||||
|
||||
✖ 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 (270.55856ms)
|
||||
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/r40c/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,207 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (211.076637ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (34.207349ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.350714ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (414.053757ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (413.816783ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (225.192824ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (190.455968ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (216.545174ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.182804ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9349.717374ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28351.01757ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (256.953613ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (134.482324ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (366.779552ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (266.29821ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (336.266747ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (103.05527ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (173.635408ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (254.629668ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (730.562698ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (181.120304ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (87.413008ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (72.272604ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.992493ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.336254ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.40581ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2538.428577ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (425.950301ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2496.560824ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4715.35978ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2475.395956ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2511.840121ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2366.15874ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5083.172933ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (722.668318ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (582.353925ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (469.567284ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (783.414104ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (570.206646ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (674.671567ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3291.984739ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (38.627999ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (353.788737ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (310.116839ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.245166ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (205.344429ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (199.68857ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (253.141895ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (281.156323ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (199.570388ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.415289ms)
|
||||
✔ 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.576386ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (297.989924ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (209.960986ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (187.738631ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (208.903124ms)
|
||||
✔ 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 (194.831672ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.748839ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (200.471243ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (212.798977ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (241.329932ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (185.815007ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.399705ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.29171ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.289316ms)
|
||||
✔ 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.489715ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.102266ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.743651ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.380971ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.395925ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.114167ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (229.019645ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (251.438005ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (271.794692ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (843.897866ms)
|
||||
✔ H4: self-takeover is refused (188.864508ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (450.483164ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1013.561874ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (194.938472ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (581.284797ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (250.971159ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (117.947836ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (178.224438ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (565.102466ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (266.873236ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (197.785824ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (788.793455ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (364.242198ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (765.518356ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (274.096848ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (467.238094ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.644438ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (696.98591ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (534.823714ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (663.255287ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2894.542634ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (579.641478ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (8.612615ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.778002ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.622306ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.516118ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.78982ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.17778ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (2.12895ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.657323ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.070213ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.254685ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.034599ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.107931ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.063808ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.291747ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.769306ms)
|
||||
✔ F9: registrations never add or redirect a root (1.621776ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.099532ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.712883ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.180716ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.413887ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.44242ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.7474ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (559.266436ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.92926ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.593501ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.507037ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.171811ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.060714ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (775.962247ms)
|
||||
✔ the engine pin holds for the installed package (1.60809ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (290.183478ms)
|
||||
✔ 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) (562.892909ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (239.923521ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (463.94818ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (493.384913ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (234.432969ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.044727ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (332.232127ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (217.059881ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (291.095661ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (452.398096ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1872.133223ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (185.106039ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (267.104913ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (135.381071ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (201.557644ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (387.186013ms)
|
||||
✔ N10: fake conformance (32.738253ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (411.430629ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (262.717708ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (307.175677ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (400.04523ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (349.142564ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (522.621265ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (244.025144ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (416.122347ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (617.068048ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (546.464894ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (439.663588ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (202.543546ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (194.958221ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (203.023052ms)
|
||||
✖ 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.898095ms)
|
||||
✖ 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.214769ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 159
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 44308.337232
|
||||
|
||||
✖ 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.898095ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception.
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/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.214769ms)
|
||||
AssertionError [ERR_ASSERTION]: Missing expected exception: {"command":"/bin/sh"}
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/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,208 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (210.124284ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (31.461327ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.243448ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (402.28461ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (375.898502ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (245.697874ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (205.248272ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (200.967855ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.300381ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10019.459164ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28021.743344ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (293.52326ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (170.965273ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (467.834848ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (485.764314ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (357.426464ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (112.120278ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (179.999177ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (280.218842ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (794.918787ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (236.226156ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (99.019273ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (72.239023ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.233754ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.07543ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.784372ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2543.005043ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (427.579138ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2546.039593ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4729.425819ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2517.647784ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2456.065654ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2397.946545ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5013.495174ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (665.282732ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (565.141189ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (449.546236ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (580.520202ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (353.242935ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (440.882452ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3289.779978ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (38.995157ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (316.778234ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (297.186173ms)
|
||||
✖ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (1.291193ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (203.795654ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (199.791926ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (221.921228ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (285.806839ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (183.605566ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.433561ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (200.894322ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (301.700912ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (204.230931ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (175.521114ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (208.038225ms)
|
||||
✔ 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.614244ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.007138ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (205.327098ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (221.041919ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (240.904641ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (193.703865ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.429915ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.332324ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.304665ms)
|
||||
✔ 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.487566ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.002825ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.815852ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.380236ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.370888ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.105714ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (219.132478ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (244.714778ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (287.860814ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (810.97954ms)
|
||||
✔ H4: self-takeover is refused (196.716399ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (428.252517ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1026.91514ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (213.614887ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (707.448266ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (323.714535ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (280.799125ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (227.071722ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (581.862104ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (284.28098ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (200.131163ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (876.966075ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (352.507519ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (926.853888ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (252.575877ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (378.482834ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.743173ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (694.916678ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (634.538003ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (797.111125ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2923.654555ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (599.855939ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.541456ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.547317ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.519194ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (4.029671ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.477817ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.163806ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.954376ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.885908ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.039207ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.311931ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.962792ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.922015ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.083999ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.697783ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.940209ms)
|
||||
✔ F9: registrations never add or redirect a root (1.617335ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.061892ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.738695ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.22052ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.070584ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.104794ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.623633ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (537.366682ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.390694ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.475862ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.128417ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (0.993087ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.835524ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (781.261909ms)
|
||||
✔ the engine pin holds for the installed package (1.814596ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (251.074264ms)
|
||||
✔ 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) (539.885355ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (250.831787ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (467.221861ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (456.140571ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (216.781065ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.790662ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (359.505509ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (198.253475ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (290.020188ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (461.038595ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1999.044479ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (243.055993ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (307.828333ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (206.716102ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (179.929887ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (391.461287ms)
|
||||
✔ N10: fake conformance (32.726895ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (437.718821ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (283.355298ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (287.744185ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (357.831472ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (429.130986ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (504.83989ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (248.28802ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (324.654472ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (1053.254692ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (651.115448ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (530.842773ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (229.950926ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (212.523964ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (219.580102ms)
|
||||
✔ 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 (169.353006ms)
|
||||
✖ 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 (161.499097ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 159
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 45197.746311
|
||||
|
||||
✖ 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.291193ms)
|
||||
AssertionError [ERR_ASSERTION]: XDG_RUNTIME_DIR
|
||||
|
||||
true !== false
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/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 (161.499097ms)
|
||||
AssertionError [ERR_ASSERTION]: NODE_TEST_CONTEXT
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/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,191 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (205.243368ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.971124ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.121787ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (363.092035ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (399.908161ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (258.536463ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (191.324727ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (198.171459ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.298721ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9540.859249ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28438.460806ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (236.541889ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (140.833118ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (352.485068ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (274.634527ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (332.945439ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (99.79432ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (168.079107ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (257.196308ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (707.796162ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (177.200989ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (87.206565ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (69.800339ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.739859ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.120244ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.763373ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2570.068602ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (427.02865ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2497.435582ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4713.208018ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2477.606579ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2437.935691ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2369.057138ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4841.240628ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (908.480524ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (686.740273ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (442.584651ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (983.93316ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (539.705712ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (518.578873ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3273.76047ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (38.212767ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (355.279405ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (296.183553ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.466304ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (209.004856ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (214.137217ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (201.61086ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (272.875125ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (190.064034ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.796296ms)
|
||||
✔ 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.574027ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (295.268898ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (209.386053ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (189.16872ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (219.84494ms)
|
||||
✔ 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 (192.081571ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.677012ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (207.298698ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (222.116357ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (242.216792ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (201.786645ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.416713ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.293798ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.319004ms)
|
||||
✔ 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.490272ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.087181ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.776708ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.392342ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.402684ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.124997ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (220.254316ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (251.23024ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (307.429041ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (791.07685ms)
|
||||
✔ H4: self-takeover is refused (191.115619ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (438.949148ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1037.817938ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (230.089636ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (639.672206ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (288.784191ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (177.231887ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (182.57847ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (558.827796ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (273.421679ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (186.077439ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (822.301611ms)
|
||||
✖ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (157.367344ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (709.37497ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (246.481901ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (411.858876ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.557871ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (738.819045ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (666.805349ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (714.737813ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2904.453949ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (585.679185ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.539397ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.689488ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.045989ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (4.213632ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (2.241709ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.724827ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.968384ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (2.018734ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (5.153028ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.840292ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.351711ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.161304ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.900902ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.955017ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.310891ms)
|
||||
✔ F9: registrations never add or redirect a root (1.669766ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.578602ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.842248ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.63283ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.496616ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.295625ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.728643ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (530.342879ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.537373ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.413759ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.188444ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.117889ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.870566ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (882.593802ms)
|
||||
✔ the engine pin holds for the installed package (1.940486ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (287.41949ms)
|
||||
✔ 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.437299ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (276.875497ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (506.550697ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (427.410335ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (228.50453ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.288455ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (349.223622ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (201.95495ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (300.316082ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (467.772868ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1878.679275ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (175.277793ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (268.367786ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (181.146729ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (165.325529ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (335.041677ms)
|
||||
✔ N10: fake conformance (32.262821ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (407.322016ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (259.001755ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (282.076696ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (347.971727ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (340.4005ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (413.9929ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (266.667027ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (465.879241ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (905.466694ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (574.171151ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (477.789525ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (232.373679ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (201.756147ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (179.078603ms)
|
||||
✔ 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 (210.764407ms)
|
||||
✔ 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 (135.591802ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 160
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 44476.458522
|
||||
|
||||
✖ 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) (157.367344ms)
|
||||
AssertionError [ERR_ASSERTION]: the flag is cleared on the throw
|
||||
+ actual - expected
|
||||
|
||||
+ 'stop-3139b65e069e370d'
|
||||
- null
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/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-3139b65e069e370d',
|
||||
expected: null,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (203.41786ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (30.836515ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.975065ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (353.712488ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (393.124663ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (251.188238ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (195.475876ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (183.457034ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.376639ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9602.448409ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (27952.100317ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (262.313942ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (144.571215ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (457.743846ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (344.484377ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (379.903393ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (126.943466ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (180.234541ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (452.027821ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (770.05324ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (205.609325ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (92.540488ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (74.345949ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.766246ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.129371ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.154138ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2539.100458ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (426.885276ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2527.265453ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4720.852699ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2489.036321ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2637.638113ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2399.580384ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4926.638042ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (678.124747ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (553.076107ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (426.134464ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (607.694768ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (389.844702ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (467.72868ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3292.379509ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (41.147415ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (321.698953ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (292.313267ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.679306ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (202.198347ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (190.064711ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (199.281057ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (274.346645ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (196.747279ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.484386ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (191.456364ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (297.864038ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (201.443252ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (199.763498ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (202.958219ms)
|
||||
✔ 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 (208.068961ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.513842ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (192.192445ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (217.978453ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (243.384518ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (199.20547ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.399352ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.266554ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.314843ms)
|
||||
✔ 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.481182ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.097987ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.804565ms)
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.384249ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.40751ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.109987ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (227.591853ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (247.828826ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (297.856813ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (783.576315ms)
|
||||
✔ H4: self-takeover is refused (193.79138ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (446.84611ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1023.235481ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (233.460026ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (685.766623ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (314.349745ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (226.566774ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (214.067602ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (605.624418ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (276.653331ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (196.280549ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (865.003311ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (329.918418ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (761.550873ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (209.420542ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (379.313842ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.605402ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (720.00289ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (631.521911ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (731.925245ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2915.844105ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (576.100997ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.523027ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.615618ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.584543ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.144481ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.285966ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.639214ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.665467ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.055042ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.842976ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.510932ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (6.677964ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.737218ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.559182ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.704012ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.69923ms)
|
||||
✔ F9: registrations never add or redirect a root (1.633646ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.037666ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.730933ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.164068ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.049537ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.299441ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.756733ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (513.925774ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.425573ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.385008ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.103343ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.001552ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.770477ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (799.018968ms)
|
||||
✔ the engine pin holds for the installed package (1.61508ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (259.435881ms)
|
||||
✔ 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) (525.770955ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (260.997836ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (460.188648ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (428.517568ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (225.28543ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.08887ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (346.262404ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (189.447164ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (305.267892ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (458.470205ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1865.195374ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (225.777454ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (301.828835ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (206.4257ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (225.01419ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (357.412059ms)
|
||||
✔ N10: fake conformance (32.803959ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (434.616033ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (282.522584ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (284.534935ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (381.37991ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (351.13522ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (351.419345ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (220.666817ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (443.614269ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (851.415212ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (589.727861ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (496.127291ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (260.787049ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (208.774599ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (191.207041ms)
|
||||
✔ 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 (225.124882ms)
|
||||
✔ 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 (181.956861ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 160
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 44600.400016
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:729:1
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.384249ms)
|
||||
Error: not connected
|
||||
at stub.takeover (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:735:28)
|
||||
at #act (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/src/terminal.mjs:226:80)
|
||||
at file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/src/terminal.mjs:213:37
|
||||
at #run (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/src/terminal.mjs:148:17)
|
||||
at file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/src/terminal.mjs:131: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/r40c/wt/packages/conversation/tests/flows.test.mjs:739: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,249 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (204.054961ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (34.379721ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.823333ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (368.03835ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (381.961473ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (248.075537ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (194.16258ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (199.892251ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.409743ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9646.508618ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28683.355079ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (277.997362ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (150.982432ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (385.987741ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (309.420051ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (536.513285ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (222.313753ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (281.830815ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (539.996757ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1373.317081ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (198.122522ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (90.321015ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (70.600173ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.23192ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.107839ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.776203ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2525.510435ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (416.595939ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2525.015114ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4731.593488ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2492.593649ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2427.004346ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2344.666336ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4947.565009ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (718.282118ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (701.816499ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (494.381035ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (774.978815ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (378.622296ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (912.631395ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3356.226708ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (22.665709ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (330.626176ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (315.192513ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (92.815845ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (204.654339ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (229.325574ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (189.427229ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (260.756963ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (184.740503ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.449448ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (181.966044ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (295.124047ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (206.003713ms)
|
||||
✔ 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.664141ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (200.892038ms)
|
||||
✔ 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 (213.646988ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.786118ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (207.464846ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (228.324171ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (233.822231ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (198.428572ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.458166ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.303444ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.387812ms)
|
||||
✖ 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.176916ms)
|
||||
✖ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (10.235668ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.082787ms)
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.518385ms)
|
||||
✖ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.350038ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.110775ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (271.453398ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (249.859212ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (312.273393ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (769.744797ms)
|
||||
✔ H4: self-takeover is refused (189.998676ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (423.547507ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1030.02182ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (226.142835ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (610.381036ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (272.715979ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (137.283715ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (189.150552ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (551.849378ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (362.37526ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (209.161203ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (837.300297ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (336.911795ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (720.420356ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (191.926492ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (327.649024ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.625546ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (665.032178ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (575.669043ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (756.784345ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3010.580645ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (592.373753ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (9.103468ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.657269ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.743898ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.820379ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (2.239145ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (3.168417ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (2.845088ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.491415ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.654332ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.067451ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.484527ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.33199ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.676737ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.858039ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.957907ms)
|
||||
✔ F9: registrations never add or redirect a root (1.487184ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.962499ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.778615ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.766097ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.522762ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.169327ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.69808ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (499.952617ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.66236ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.422413ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.261312ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.01753ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.860287ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (842.463988ms)
|
||||
✔ the engine pin holds for the installed package (2.852236ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (259.28921ms)
|
||||
✔ 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) (512.779416ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (292.244852ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (505.271246ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (411.28012ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (225.967067ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.503213ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (342.057922ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (196.49213ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (290.083993ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (477.6962ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1887.787322ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (185.311504ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (266.673099ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (226.462671ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (288.90147ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (400.513262ms)
|
||||
✔ N10: fake conformance (31.406735ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (426.755647ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (266.170917ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (277.390485ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (348.249457ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (355.074434ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (344.930861ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (140.398913ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (312.785854ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (770.727899ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (582.312023ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (548.270857ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (282.639537ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (199.232135ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (211.15431ms)
|
||||
✔ 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 (208.274873ms)
|
||||
✔ 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 (195.561529ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 157
|
||||
ℹ fail 4
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 46343.156165
|
||||
|
||||
✖ 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.176916ms)
|
||||
AssertionError [ERR_ASSERTION]: text typed after the takeover is sent
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'hi'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/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:680:1
|
||||
✖ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (10.235668ms)
|
||||
AssertionError [ERR_ASSERTION]: ["\u0007","\u0014","hi\r"]: the text is still held
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:704:12)
|
||||
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: null,
|
||||
expected: null,
|
||||
operator: 'notStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:729:1
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.518385ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'hi'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:744: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'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:754:1
|
||||
✖ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.350038ms)
|
||||
AssertionError [ERR_ASSERTION]: ["\u0007\u0014hi\r"]
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'hi'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:767:12)
|
||||
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'
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (444.489241ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.114009ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (20.803292ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (437.3869ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (401.575514ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (241.557301ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (218.171279ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (200.467573ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.353538ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9625.491807ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (30328.453661ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (572.328261ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (226.015185ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (557.996747ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (430.320709ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (482.984396ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (191.905232ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (270.708722ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (721.95802ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1366.114447ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (339.840753ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (115.697426ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (87.362233ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.893589ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.089914ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.002417ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3284.999053ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (448.989865ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2785.741884ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4828.594925ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2420.879439ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2502.580258ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2375.057737ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5161.12547ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (725.127878ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (565.417166ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (498.327322ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (1251.927386ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (396.063665ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (480.77333ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3389.883044ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (60.657475ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (833.090385ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (534.478549ms)
|
||||
✖ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (2.927391ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (448.541556ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (207.475064ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (253.805985ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (305.816352ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (189.35486ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.430791ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (192.991854ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (318.916082ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (229.654079ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (214.346593ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (211.145867ms)
|
||||
✔ 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.990503ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.9355ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (219.181602ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (231.586346ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (267.004811ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (211.054009ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.489353ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.298263ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.341813ms)
|
||||
✔ 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.519375ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.362917ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.875831ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.476889ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.447666ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.108257ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (233.110746ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (490.744892ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (336.996034ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (839.949699ms)
|
||||
✔ H4: self-takeover is refused (214.245529ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (476.054713ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1095.220116ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (244.046467ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (720.633738ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (284.51342ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (199.635372ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (201.556848ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (591.750049ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (283.396543ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (205.07029ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (943.169717ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (353.797434ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (753.240116ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (192.330506ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (330.733447ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.728618ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (626.777369ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (510.956703ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (648.387826ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2925.028324ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (578.300644ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.498062ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.491119ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.449653ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.13226ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.346238ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.831007ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.690639ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.045602ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.382775ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.43909ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.478704ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.358496ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.594726ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.624349ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.999087ms)
|
||||
✔ F9: registrations never add or redirect a root (1.482234ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.240941ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.827434ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.240274ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.797786ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.325569ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.833734ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (546.515998ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.531678ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.42695ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.178693ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.082437ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.026942ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (770.378589ms)
|
||||
✔ the engine pin holds for the installed package (1.717473ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (285.09891ms)
|
||||
✔ 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) (617.244257ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (236.213558ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (775.316092ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (476.949478ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (235.879383ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.939981ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (373.789641ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (215.369081ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (314.808142ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (508.15758ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1840.598738ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (200.532378ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (293.354862ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (208.14776ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (213.690716ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (372.326326ms)
|
||||
✔ N10: fake conformance (33.462595ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (445.339651ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (314.724531ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (306.15793ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (374.286004ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (364.268546ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (348.461093ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (183.341635ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (350.625202ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (598.023105ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (494.015569ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (455.604102ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (200.048152ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (194.431362ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (183.07013ms)
|
||||
✔ 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 (219.335665ms)
|
||||
✔ 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 (217.267209ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 160
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 49224.239848
|
||||
|
||||
✖ 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.927391ms)
|
||||
Error: the scope exited before the shim answered ({"code":1,"signal":null})
|
||||
at ScopeLauncher.launch (file:///home/jwoltje/darkwing-scratch/r40c/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/r40c/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,51 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3951.299513ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4145.685127ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5815.89726ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (248.246257ms)
|
||||
✖ bus routes are GET-only, have no write verb, and keep the same-origin checks (14.331615ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (58.423309ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2583.156034ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (359.158216ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (529.2833ms)
|
||||
✔ serve refuses a --business value that is not a business id (174.187442ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3887.05143ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2076.508456ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1083.482735ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (797.718462ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3790.919426ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54379.458865ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3797.085056ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21890.363687ms)
|
||||
✔ loopback host and board origin fail closed (9.021567ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (105.753227ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (73.398536ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1461.685607ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 21
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54809.510942
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:90:1
|
||||
✖ bus routes are GET-only, have no write verb, and keep the same-origin checks (14.331615ms)
|
||||
AssertionError [ERR_ASSERTION]: POST
|
||||
|
||||
200 !== 405
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/webui/tests/bus.test.mjs:96: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,56 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3003.3722ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3114.111063ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4351.889677ms)
|
||||
✖ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (276.696684ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (388.203302ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (118.075829ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2288.9815ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (113.110792ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (538.538233ms)
|
||||
✔ serve refuses a --business value that is not a business id (158.489801ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2969.53414ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1655.234956ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1160.899414ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (841.814023ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3093.32565ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53481.922902ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3084.069667ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21702.694009ms)
|
||||
✔ loopback host and board origin fail closed (7.724318ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (150.087521ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (349.28937ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1217.653677ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 21
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54048.101847
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:49:1
|
||||
✖ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (276.696684ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'',
|
||||
+ 'invalid-response'
|
||||
- 'invalid-request'
|
||||
]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/webui/tests/bus.test.mjs:85: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,31 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (2455.957142ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (2677.423888ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4179.839474ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (242.675976ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (221.800586ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (108.096567ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2372.69171ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (85.668748ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.652335ms)
|
||||
✔ serve refuses a --business value that is not a business id (166.099183ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2520.247071ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1708.93403ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1129.849334ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (841.107983ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2617.740385ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53316.629363ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2648.859909ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21717.140369ms)
|
||||
✔ loopback host and board origin fail closed (8.35513ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (99.237921ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (78.046439ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1223.612451ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 22
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53676.103745
|
||||
@@ -0,0 +1,52 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (2596.627211ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (2656.24458ms)
|
||||
✖ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (2893.434971ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (285.55456ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (99.023379ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (239.181445ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2424.804603ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (80.061707ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (527.407691ms)
|
||||
✔ serve refuses a --business value that is not a business id (154.360857ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2573.003601ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1559.783589ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1021.105609ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (724.020956ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2444.135774ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53415.269145ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2473.191827ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21916.831767ms)
|
||||
✔ loopback host and board origin fail closed (6.582995ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (93.174082ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (66.499708ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1234.234867ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 21
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53852.203465
|
||||
|
||||
✖ 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 (2893.434971ms)
|
||||
AssertionError [ERR_ASSERTION]: decision
|
||||
|
||||
2 !== 0
|
||||
|
||||
at inertCheck (file:///home/jwoltje/darkwing-scratch/r40c/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/r40c/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,47 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3692.862605ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4399.78142ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5138.900983ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (240.785812ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (229.332019ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (137.490595ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2050.349367ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (255.148435ms)
|
||||
✖ humanCall kills a transport that runs past its timeout (1534.982657ms)
|
||||
✔ serve refuses a --business value that is not a business id (173.289267ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3713.717292ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2111.922539ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1135.55318ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (796.483555ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4531.298745ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54222.404514ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4539.35007ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21792.672553ms)
|
||||
✔ loopback host and board origin fail closed (6.686818ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (99.11453ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (99.935713ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1076.900081ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 21
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 61469.598771
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:196:1
|
||||
✖ humanCall kills a transport that runs past its timeout (1534.982657ms)
|
||||
AssertionError [ERR_ASSERTION]: transport 2130788 still runs after the timeout
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/webui/tests/bus.test.mjs:207:10)
|
||||
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,54 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4083.605735ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4274.417716ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5604.426858ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (298.719751ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (455.827898ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (98.277335ms)
|
||||
✖ humanCall speaks the human transport protocol without blocking the server (824.313532ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (260.502986ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (537.16016ms)
|
||||
✔ serve refuses a --business value that is not a business id (160.84085ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4074.532968ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2021.581003ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1166.832554ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (874.161758ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3919.185383ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54454.659071ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3922.938725ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21951.170269ms)
|
||||
✔ loopback host and board origin fail closed (6.998828ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (94.95425ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (149.515077ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1384.946165ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 21
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54798.301609
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:149:1
|
||||
✖ humanCall speaks the human transport protocol without blocking the server (824.313532ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'resolved'
|
||||
- 'response-too-large'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/webui/tests/bus.test.mjs:157: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,66 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (2541.028381ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (2817.107754ms)
|
||||
✖ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (9034.138353ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (226.496809ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (75.759813ms)
|
||||
✖ bus refusals map to statuses and never carry a path or a stack (9.012812ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2714.39824ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (115.769104ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.388769ms)
|
||||
✔ serve refuses a --business value that is not a business id (179.231846ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2458.233556ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1643.141896ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1910.878603ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (814.993352ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2407.206463ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (52937.436576ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2420.954806ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21957.117114ms)
|
||||
✔ loopback host and board origin fail closed (6.856516ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (85.519283ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (74.83407ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1375.540306ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 20
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53283.086833
|
||||
|
||||
✖ 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 (9034.138353ms)
|
||||
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/r40c/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/r40c/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:106:1
|
||||
✖ bus refusals map to statuses and never carry a path or a stack (9.012812ms)
|
||||
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/r40c/wt/packages/webui/tests/bus.test.mjs:117: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,54 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (2694.332875ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (2786.251338ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4198.779179ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (248.724539ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (109.760828ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (333.4259ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2558.626957ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (80.256537ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (526.387072ms)
|
||||
✖ serve refuses a --business value that is not a business id (10054.738579ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2643.637933ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1519.888206ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1034.895305ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (782.595368ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2390.525218ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53307.043172ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2409.634346ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21953.548833ms)
|
||||
✔ loopback host and board origin fail closed (8.953531ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (105.696867ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (111.406083ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1529.056823ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 21
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53688.834121
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/webui/tests/bus.test.mjs:210:1
|
||||
✖ serve refuses a --business value that is not a business id (10054.738579ms)
|
||||
AssertionError [ERR_ASSERTION]: ../acme
|
||||
|
||||
0 !== 2
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/webui/tests/bus.test.mjs:214: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 async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 0,
|
||||
expected: 2,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (217.396936ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (36.544565ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (21.507903ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (509.001469ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (404.163675ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (242.40283ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (208.807864ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (207.462225ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.318314ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10232.482422ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (31466.722864ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (335.284664ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (188.849899ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (450.670767ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (301.822469ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (340.189331ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (105.122836ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (171.631828ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (267.469163ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1004.22294ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (312.798794ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (148.578052ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (74.804698ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.46135ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.298974ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.793936ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2672.692957ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (479.782186ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2575.098594ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4782.558278ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2508.741174ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2635.517593ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2543.327683ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5117.317392ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (766.257735ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (703.483815ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (493.852682ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (838.499197ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (441.909021ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (627.890156ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3359.232229ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (40.675614ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (376.772979ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (413.568482ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.72845ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (221.922644ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (215.880869ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (329.259642ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (294.562439ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (215.950216ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.574922ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (211.859135ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (308.417243ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (217.735365ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (197.885125ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (227.209087ms)
|
||||
✔ 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.067899ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.068684ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (239.108372ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (242.591519ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (280.448759ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (239.877174ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.424539ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.282157ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.314412ms)
|
||||
✔ 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.507614ms)
|
||||
✖ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (11.025845ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (6.302905ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.415843ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.389792ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.123437ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (222.524377ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (270.754078ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (424.971884ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (848.438172ms)
|
||||
✔ H4: self-takeover is refused (199.017247ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (459.423631ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1159.721239ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (284.251468ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (976.17191ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (385.642926ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (201.31102ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (204.974928ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (644.134693ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (309.222357ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (228.643207ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (982.627072ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (522.547076ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (851.975086ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (227.017042ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (396.824157ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.630306ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (679.062429ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (568.330752ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (760.632219ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3076.593599ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (782.600239ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (8.750856ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.808138ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.23454ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.774025ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.88316ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.184033ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.801433ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.342579ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.922936ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.21217ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (6.510266ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.903471ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.670331ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.537823ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.006251ms)
|
||||
✔ F9: registrations never add or redirect a root (1.495698ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.236016ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.953874ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.087118ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.452353ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.357137ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.710358ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (529.313976ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.313232ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.39972ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.202044ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (0.986074ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.765077ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (748.061593ms)
|
||||
✔ the engine pin holds for the installed package (2.026407ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (280.796856ms)
|
||||
✔ 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) (593.646992ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (240.141321ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (642.989325ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (450.085349ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (253.658679ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.048937ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (367.649695ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (211.176137ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (297.370414ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (513.73479ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1956.058807ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (193.257577ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (305.80327ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (203.839986ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (244.440611ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (434.025175ms)
|
||||
✔ N10: fake conformance (30.855908ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (478.715547ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (299.164409ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (316.810557ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (417.870515ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (523.550464ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (418.666842ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (210.675714ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (412.485444ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (846.702599ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (610.708731ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (561.1203ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (282.574801ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (333.782942ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (222.214704ms)
|
||||
✔ 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 (260.396141ms)
|
||||
✔ 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 (203.986666ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 160
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 49112.138906
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:680:1
|
||||
✖ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (11.025845ms)
|
||||
AssertionError [ERR_ASSERTION]: ["\u0007","\u0014","hi\r"]: the text is still held
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:704:12)
|
||||
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: null,
|
||||
expected: null,
|
||||
operator: 'notStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (214.364681ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (34.729116ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.669713ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (444.981908ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (385.441519ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (235.156238ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (197.163272ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (202.451129ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.287062ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10475.640184ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (31591.493553ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (347.511748ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (216.811217ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (529.922331ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (363.123069ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (477.537801ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (190.129522ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (247.113343ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (558.514571ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1401.855724ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (331.477014ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (123.99506ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (93.586794ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.722953ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.08199ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.941273ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3045.060309ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (547.16907ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2994.327317ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4948.134867ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2493.863571ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2613.418738ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2475.844123ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5192.741498ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (995.950666ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (745.643408ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (687.714102ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (1015.407081ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (648.192036ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (501.900788ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3273.802319ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (40.464229ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (334.892716ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (305.895112ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (63.442695ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (208.533815ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (201.238056ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (273.500065ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (283.65704ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (198.182318ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.412127ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (187.380019ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (302.657846ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (208.393901ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (202.517874ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (200.696044ms)
|
||||
✔ 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 (208.982831ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.655091ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (211.345338ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (235.583819ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (255.215608ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (281.307156ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.374461ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.282956ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.292843ms)
|
||||
✔ 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.468686ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.776404ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.664671ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.314455ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.36567ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.112226ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (233.564714ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (257.108257ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (281.278331ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (852.719203ms)
|
||||
✔ H4: self-takeover is refused (187.732732ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (443.496829ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1060.991154ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (278.839204ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1184.532916ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (353.164261ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (194.182751ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (209.626616ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (631.035486ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (295.166919ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (205.602705ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (990.571478ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (421.756016ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (860.018621ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (184.053928ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (357.436635ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.630687ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (659.026885ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (578.43071ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (748.558247ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2968.871933ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (671.19246ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.514664ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.809684ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.699252ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.465703ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.510913ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.917931ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.764385ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.294389ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.311561ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.900826ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.406131ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.405912ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.411023ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.640201ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.732751ms)
|
||||
✔ F9: registrations never add or redirect a root (1.645064ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.037322ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.708086ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.848189ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.967854ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.380699ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.920617ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (532.68293ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.40052ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.364256ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.135617ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (0.983498ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.803055ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (750.611034ms)
|
||||
✔ the engine pin holds for the installed package (1.871052ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (267.127695ms)
|
||||
✔ 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.759336ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (244.669222ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (486.531938ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (514.964805ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (230.069796ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.122058ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (345.070237ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (197.504474ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (289.823497ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (464.644616ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1927.309525ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (206.288921ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (248.106136ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (196.491371ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (253.454964ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (404.111409ms)
|
||||
✔ N10: fake conformance (32.092362ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (395.709292ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (272.084511ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (311.87729ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (484.304304ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (414.437634ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (433.903006ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (218.334531ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (396.556351ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (811.864982ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (537.831578ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (688.752083ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (265.268271ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (228.796739ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (225.258216ms)
|
||||
✔ 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.264821ms)
|
||||
✔ 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 (221.697341ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 161
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 50589.291575
|
||||
@@ -0,0 +1,230 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (242.343624ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (35.697982ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (16.132322ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (503.035588ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (443.167414ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (274.703828ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (232.07581ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (230.15877ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (3.009835ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11546.378521ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (34120.00025ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (272.61315ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (153.357591ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (404.116951ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (291.262964ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (498.344358ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (100.527268ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (183.134098ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (408.823442ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1316.543463ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (192.188454ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (87.170177ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (75.31061ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.915662ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.063805ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.771087ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2988.20149ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (506.128963ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2974.04487ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5239.345122ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2737.972477ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2562.092637ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2460.787267ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5171.766888ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (823.867761ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (3524.732578ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (1031.83274ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (1193.641921ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (435.095244ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (808.437384ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3477.854872ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (32.402224ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (455.322918ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (310.652312ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.022684ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (238.403999ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (219.621604ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (303.908244ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (325.687584ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (228.402395ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.462713ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (225.651874ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (335.589092ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (227.102037ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (217.428512ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (251.797632ms)
|
||||
✔ 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.843387ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.772825ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (254.024052ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (273.039856ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (270.215357ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (340.470777ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.441265ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.286213ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.359653ms)
|
||||
✔ 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.118194ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.611268ms)
|
||||
✖ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.843415ms)
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (1.229847ms)
|
||||
✖ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.399282ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.130937ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (241.207251ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (278.928619ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (318.718404ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (962.430536ms)
|
||||
✔ H4: self-takeover is refused (238.61571ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (480.599671ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1242.995511ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (366.445367ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (960.859047ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (378.710397ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (188.588542ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (221.464586ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (691.032805ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (308.03834ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (365.854057ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1009.706804ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (612.589352ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (951.678639ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (215.145569ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (385.249977ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.642903ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (686.817238ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (559.169845ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (963.501615ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2998.854354ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (681.607038ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.721308ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.305677ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.191545ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.597571ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.680524ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.061867ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.774014ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.408537ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.478021ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.988918ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.098035ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.639327ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.512466ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.06773ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.172832ms)
|
||||
✔ F9: registrations never add or redirect a root (1.667012ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.975288ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (1.362522ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (7.322526ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (3.676783ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.359374ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.740509ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (578.559604ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.484789ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.758594ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.687208ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.265538ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.512073ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (858.408038ms)
|
||||
✔ the engine pin holds for the installed package (2.78358ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (302.995856ms)
|
||||
✔ 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) (585.010443ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (284.883315ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (532.607254ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (580.898622ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (274.671144ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.18143ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (417.589193ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (222.373716ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (325.759633ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (554.614239ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1975.221782ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (227.991096ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (315.828165ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (235.040587ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (267.803978ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (469.961288ms)
|
||||
✔ N10: fake conformance (31.485735ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (627.386401ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (334.222906ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (311.277382ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (553.481603ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (494.790784ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (479.029771ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (234.327841ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (396.661213ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (828.119974ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (599.354327ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (831.434088ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (288.73939ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (224.631089ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (216.287649ms)
|
||||
✔ 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 (259.470155ms)
|
||||
✔ 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 (215.481299ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 158
|
||||
ℹ fail 3
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53597.577915
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:714:1
|
||||
✖ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.843415ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
'x\r' !== null
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:724: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: 'x\r',
|
||||
expected: null,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:729:1
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (1.229847ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'hi'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:744: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'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:754:1
|
||||
✖ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.399282ms)
|
||||
AssertionError [ERR_ASSERTION]: ["\u0007\u0014hi\r"]
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'hi'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:767:12)
|
||||
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'
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (198.372123ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (31.075337ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.373348ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (355.177554ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (386.29791ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (237.446176ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (183.890713ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (168.003649ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.343801ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9910.02064ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (30025.136994ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (261.020047ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (170.591496ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (427.490668ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (327.603916ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (386.852464ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (119.534133ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (221.43074ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (474.129243ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (873.351748ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (202.273476ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (87.958981ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (80.045461ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.455718ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.269502ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.851127ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2956.932282ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (409.554265ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2741.682168ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4953.800589ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2685.420378ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2520.011232ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2457.739866ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4862.365858ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (781.11378ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (644.518794ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (487.919147ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (705.807459ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (404.977896ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (838.80529ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (4277.41095ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (23.475978ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (462.780073ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (321.765474ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (61.301427ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (193.552991ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (192.007312ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (194.571228ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (288.709781ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (197.752705ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.412047ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (182.207429ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (290.391192ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (189.715577ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (181.057322ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (198.103971ms)
|
||||
✔ 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 (189.590486ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.476501ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (193.394641ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (210.509504ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (231.22304ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (192.46219ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.376815ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.260177ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.297571ms)
|
||||
✔ 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.458736ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.010788ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.777672ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.421599ms)
|
||||
✖ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.468862ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.099303ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (221.76137ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (239.465539ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (287.546555ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (801.077593ms)
|
||||
✔ H4: self-takeover is refused (172.410151ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (412.215706ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (962.843511ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (210.382637ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (698.37281ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (293.460832ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (185.903361ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (181.953284ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (562.366901ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (279.101854ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (201.806792ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1051.581951ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (444.522329ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (924.47988ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (312.45617ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (400.296077ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.620839ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (644.50411ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (543.247699ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (709.531028ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3110.410899ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (639.668441ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.053198ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.639721ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.602125ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.310523ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.455177ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.896293ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.865939ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.205055ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.274164ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.992576ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.242979ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.40804ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (11.796102ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (14.330433ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.579ms)
|
||||
✔ F9: registrations never add or redirect a root (1.723871ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.400678ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.764354ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.550319ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.385434ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.198296ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.708568ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (504.827036ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.710643ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.459074ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.108167ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.005758ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.837085ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (739.821669ms)
|
||||
✔ the engine pin holds for the installed package (1.794769ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (261.345544ms)
|
||||
✔ 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) (508.285075ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (241.64749ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (455.572594ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (431.366536ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (227.257958ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.586792ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (332.506983ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (166.500065ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (275.207131ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (441.37802ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1866.639068ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (169.536716ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (266.810678ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (191.019753ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (189.534947ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (383.401822ms)
|
||||
✔ N10: fake conformance (32.726139ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (401.412059ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (390.429835ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (389.893653ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (432.632111ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (447.361456ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (468.568149ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (313.406676ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (382.141148ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (735.547023ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (463.527134ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (520.723104ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (236.259731ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (163.030636ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (207.182651ms)
|
||||
✔ 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 (331.784523ms)
|
||||
✔ 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 (304.230941ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 160
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 47088.300087
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:754:1
|
||||
✖ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.468862ms)
|
||||
Error: boom
|
||||
at stub.interrupt (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:774:11)
|
||||
at #act (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/src/terminal.mjs:227:82)
|
||||
at file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/src/terminal.mjs:213:37
|
||||
at #run (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/src/terminal.mjs:148:17)
|
||||
at file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/src/terminal.mjs:131:65
|
||||
at async waitForActual (node:assert:615:5)
|
||||
at async strict.rejects (node:assert:738:25)
|
||||
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:780: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,190 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (351.268491ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (52.925148ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (22.624274ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (414.408093ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (479.704085ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (308.15834ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (350.06094ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (340.27323ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.240534ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11758.542981ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (30969.290753ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (388.826647ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (225.94793ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (562.638604ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (381.952208ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (549.830236ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (125.720864ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (257.84909ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (426.37923ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1597.797046ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (353.02513ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (95.291508ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (76.534257ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.777212ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.080473ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.937441ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3198.627171ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (645.327369ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (3084.924791ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5313.405745ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2527.159289ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2741.068025ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2596.518112ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5005.189729ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (849.483928ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (696.276698ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (592.815666ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (616.919564ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (308.43354ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (481.238265ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3253.744906ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (31.898634ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (353.948313ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (283.212297ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (86.03424ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (426.038841ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (237.511352ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (219.383449ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (345.90601ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (259.530822ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.516963ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (336.079309ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (455.272368ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (287.70866ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (248.887121ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (266.202438ms)
|
||||
✔ 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 (252.468016ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.782119ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (322.253869ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (396.758856ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (509.482486ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (271.821424ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.416243ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.300669ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.299783ms)
|
||||
✔ 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.517571ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.161847ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.739434ms)
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (1.010299ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.39109ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.114537ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (234.356612ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (329.076951ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (326.997833ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1132.941562ms)
|
||||
✔ H4: self-takeover is refused (356.786928ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (547.886141ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1715.517376ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (298.883867ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1055.989732ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (393.793084ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (294.394929ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (251.53385ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (904.501951ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (317.983793ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (254.29771ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1089.038778ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (426.213203ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1033.799044ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (334.232344ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (518.112129ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.776375ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (727.456025ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (609.916551ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (724.77625ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3092.95995ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (747.061881ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.918514ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.033536ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.683024ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.501767ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.795468ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.990277ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.840385ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.25527ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.714746ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (95.333905ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (6.374853ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.79805ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.25152ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.945091ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.917325ms)
|
||||
✔ F9: registrations never add or redirect a root (1.449481ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.148556ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.739579ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.438389ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.162567ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.180821ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.66641ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (574.034954ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.412441ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.405432ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.151952ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.022501ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.85587ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (880.815632ms)
|
||||
✔ the engine pin holds for the installed package (1.865873ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (293.114063ms)
|
||||
✔ 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) (599.196039ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (297.762295ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (750.026135ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (512.681898ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (293.454307ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.29253ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (638.379943ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (291.85477ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (359.362405ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (616.665022ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2223.855727ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (235.588795ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (324.917052ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (243.785601ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (325.648183ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (702.399356ms)
|
||||
✔ N10: fake conformance (33.253893ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (483.580084ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (333.019787ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (348.577398ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (471.124136ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (455.169012ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (423.051907ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (274.915158ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (488.230298ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (1199.860528ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (582.978742ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (595.051706ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (228.873049ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (192.227752ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (190.696553ms)
|
||||
✔ 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.562604ms)
|
||||
✔ 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.824721ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 160
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 52035.348439
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:729:1
|
||||
✖ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (1.010299ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
+ actual - expected
|
||||
|
||||
+ 'prompt: admitted'
|
||||
- 'failed: not connected'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:751: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: 'prompt: admitted',
|
||||
expected: 'failed: not connected',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (349.326694ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (35.195899ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (22.555716ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (417.46445ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (759.910184ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (255.490138ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (215.13466ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (220.343429ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (3.861777ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10391.602554ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (29649.81262ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (261.6005ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (147.298178ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (386.547621ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (269.857317ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (336.910644ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (108.022741ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (196.514531ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (286.016907ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (839.509482ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (199.043932ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (94.744097ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (74.023836ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.36339ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.118715ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.293078ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3320.69157ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (461.566521ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2759.720592ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5025.410566ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2513.765706ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2593.319868ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2656.485351ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4973.943053ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (759.14089ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (580.905235ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (424.841658ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (739.764604ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (354.115242ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (569.666002ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3417.469095ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (22.249903ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (384.239743ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (337.899051ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (62.152162ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (394.158946ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (229.401531ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (220.558237ms)
|
||||
✖ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (588.583704ms)
|
||||
✖ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (203.610708ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.509871ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (210.129857ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (318.592355ms)
|
||||
✖ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (228.870252ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (219.053971ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (222.876779ms)
|
||||
✔ 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 (227.31436ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.00791ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (220.066318ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (243.465125ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (275.856851ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (220.394413ms)
|
||||
✖ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.467625ms)
|
||||
✖ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.277144ms)
|
||||
✖ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.53501ms)
|
||||
✔ 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.477545ms)
|
||||
✖ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (10.195424ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.963015ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.407563ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.378982ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.102465ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (219.699407ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (279.346116ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (321.84638ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1197.806029ms)
|
||||
✔ H4: self-takeover is refused (213.250629ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (471.254671ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1152.00961ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (251.38588ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (705.40724ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (369.664002ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (219.822517ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (214.564396ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (639.102842ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (310.819701ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (228.542202ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (901.038649ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (405.416345ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (868.704509ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (219.159547ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (348.601131ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.640556ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (660.301062ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (581.695977ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (773.576823ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3130.384165ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (673.561238ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.667932ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.783514ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.729456ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.551061ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.114104ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.969484ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.862926ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.36409ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (5.229195ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (7.362052ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.187847ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.253518ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.785625ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.043299ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.880281ms)
|
||||
✔ F9: registrations never add or redirect a root (1.637364ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.577543ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (13.243468ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.538872ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.767818ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.462918ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.878114ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (554.449948ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.218644ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.552193ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.454018ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.139676ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.091055ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (796.018364ms)
|
||||
✔ the engine pin holds for the installed package (1.943661ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (277.536368ms)
|
||||
✔ 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) (602.476165ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (254.508983ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (715.093527ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (836.255359ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (249.134536ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.11236ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (383.522562ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (219.237408ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (314.142913ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (511.255907ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1934.70553ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (203.540315ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (312.744105ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (232.452897ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (236.963845ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (385.391097ms)
|
||||
✔ N10: fake conformance (32.084229ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (485.374587ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (310.486078ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (286.618921ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (405.383504ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (410.27868ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (409.718676ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (232.572275ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (391.174062ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (771.295477ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (615.528123ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (605.47142ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (249.059677ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (184.407848ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (235.350706ms)
|
||||
✔ 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 (250.279525ms)
|
||||
✔ 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 (217.941414ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 154
|
||||
ℹ fail 7
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 47469.552169
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:129:1
|
||||
✖ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (588.583704ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
'/null' !== '/'
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:135: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: '/null',
|
||||
expected: '/',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:156:1
|
||||
✖ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (203.610708ms)
|
||||
AssertionError [ERR_ASSERTION]: a paste is literal and never submits by itself
|
||||
+ actual - expected
|
||||
|
||||
+ 'run the deploy\n/goal xnull'
|
||||
- 'run the deploy\n/goal x'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:169:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: 'run the deploy\n/goal xnull',
|
||||
expected: 'run the deploy\n/goal x',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:243:1
|
||||
✖ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (228.870252ms)
|
||||
AssertionError [ERR_ASSERTION]: the draft is kept
|
||||
+ actual - expected
|
||||
|
||||
+ 'half-typed draftnull'
|
||||
- 'half-typed draft'
|
||||
^
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:262:12)
|
||||
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: 'half-typed draftnull',
|
||||
expected: 'half-typed draft',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:522:1
|
||||
✖ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.467625ms)
|
||||
AssertionError [ERR_ASSERTION]: an empty Enter sends nothing; nothing is resent
|
||||
|
||||
2 !== 1
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:542: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: 2,
|
||||
expected: 1,
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:559:1
|
||||
✖ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.277144ms)
|
||||
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
|
||||
|
||||
'null' !== ''
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:565: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: 'null',
|
||||
expected: '',
|
||||
operator: 'strictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:576:1
|
||||
✖ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.53501ms)
|
||||
AssertionError [ERR_ASSERTION]: cut 1: nothing sent
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ 'ull[200~a'
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:583: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: [ 'ull[200~a' ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:680:1
|
||||
✖ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (10.195424ms)
|
||||
AssertionError [ERR_ASSERTION]: ["\u0007","\u0014","hi\r"]: the text is still held
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r40c/wt/packages/conversation/tests/flows.test.mjs:704:12)
|
||||
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: null,
|
||||
expected: null,
|
||||
operator: 'notStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
cd ~/darkwing-scratch/r40c/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 Mq-drain-any Mr-no-recompute Ms-no-try Mt-held-rethrow Mu-no-report Mv-feed-holds-true; 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-r3.sha256 2>&1 | grep -c ': OK$' > ../mut/manifest-after.txt
|
||||
echo DONE >> ../mut/summary.txt
|
||||
@@ -0,0 +1 @@
|
||||
27
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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"),
|
||||
"Mq-drain-any": (C + "terminal.mjs", " if (!holds) break;\n", " if (this.held === null) break;\n"),
|
||||
"Mr-no-recompute": (C + "terminal.mjs", " actions = this.#parse(rest);\n holds = this.held !== null;\n", " actions = this.#parse(rest);\n holds = false;\n"),
|
||||
"Ms-no-try": (C + "terminal.mjs", " try {\n await run();\n } catch (err) {\n if (report) report(err);\n else failure ??= err;\n }\n", " await run();\n"),
|
||||
"Mt-held-rethrow": (C + "terminal.mjs", " this.held += data;\n return this.queue.catch(() => {});\n", " this.held += data;\n return this.queue;\n"),
|
||||
"Mu-no-report": (C + "terminal.mjs", " if (report) report(err);\n else failure ??= err;\n", " failure ??= err;\n"),
|
||||
"Mv-feed-holds-true": (C + "terminal.mjs", " const holds = this.held !== null;\n", " const holds = true;\n"),
|
||||
}
|
||||
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/r40c/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,23 @@
|
||||
Ma-no-approve (packages/conversation/src/pi-pin.mjs): conversation rc 1 pass 160 fail 1
|
||||
Mb-seal-command (packages/conversation/src/controller.mjs): conversation rc 1 pass 160 fail 1
|
||||
Mc-engine-keys (packages/conversation/src/controller.mjs): conversation rc 1 pass 159 fail 2
|
||||
Md-env-inherit (packages/conversation/src/pi-pin.mjs): conversation rc 1 pass 159 fail 2
|
||||
Me-escalating (packages/conversation/src/controller.mjs): conversation rc 1 pass 160 fail 1
|
||||
Mf-queue-catch (packages/conversation/src/terminal.mjs): conversation rc 1 pass 160 fail 1
|
||||
Mg-no-hold (packages/conversation/src/terminal.mjs): conversation rc 1 pass 157 fail 4
|
||||
Mh-manager-env (packages/conversation/src/cohort.mjs): conversation rc 1 pass 160 fail 1
|
||||
Mq-drain-any (packages/conversation/src/terminal.mjs): conversation rc 1 pass 160 fail 1
|
||||
Mr-no-recompute (packages/conversation/src/terminal.mjs): conversation rc 0 pass 161 fail 0
|
||||
Ms-no-try (packages/conversation/src/terminal.mjs): conversation rc 1 pass 158 fail 3
|
||||
Mt-held-rethrow (packages/conversation/src/terminal.mjs): conversation rc 1 pass 160 fail 1
|
||||
Mu-no-report (packages/conversation/src/terminal.mjs): conversation rc 1 pass 160 fail 1
|
||||
Mv-feed-holds-true (packages/conversation/src/terminal.mjs): conversation rc 1 pass 154 fail 7
|
||||
Mi-bus-post (packages/webui/src/serve.mjs): webui rc 1 pass 21 fail 1
|
||||
Mj-subject (packages/webui/src/serve.mjs): webui rc 1 pass 21 fail 1
|
||||
Mk-copy-safe (packages/webui/src/public/bus.js): webui rc 0 pass 22 fail 0
|
||||
Ml-option-inert (packages/webui/src/public/bus.js): webui rc 1 pass 21 fail 1
|
||||
Mm-timeout-kill (packages/webui/src/bus.mjs): webui rc 1 pass 21 fail 1
|
||||
Mn-max-bytes (packages/webui/src/bus.mjs): webui rc 1 pass 21 fail 1
|
||||
Mo-status (packages/webui/src/bus.mjs): webui rc 1 pass 20 fail 2
|
||||
Mp-business-id (packages/webui/src/cli.mjs): webui rc 1 pass 21 fail 1
|
||||
DONE
|
||||
@@ -0,0 +1,132 @@
|
||||
✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (11.497625ms)
|
||||
✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.292748ms)
|
||||
✔ completed smoke replies and ordinary questions are idle, not human blockers (0.73417ms)
|
||||
✔ only an explicit first-line input request makes a finished reply waiting (0.151844ms)
|
||||
✔ tool activity, user text, errors and unfinished turns override attention text (0.126081ms)
|
||||
✔ STOP access failure is unknown, not absence, under a non-root identity (35.372807ms)
|
||||
✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (2.626802ms)
|
||||
✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.269756ms)
|
||||
✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (5.602663ms)
|
||||
✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.543234ms)
|
||||
✔ server rescans connector discovery and refuses HTTP reply without transport (34.032559ms)
|
||||
✔ connector session links and linked directories are not read (1.163038ms)
|
||||
✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (2.952802ms)
|
||||
✔ CLI print uses the relaunch notice instead of old current preview (58.598175ms)
|
||||
✔ connector owner and fixed task never inherit a native relaunch notice (2.055349ms)
|
||||
✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.070293ms)
|
||||
✔ loadConfig: missing file throws ConfigError (1.597423ms)
|
||||
✔ loadConfig: invalid JSON throws ConfigError (0.335575ms)
|
||||
✔ loadConfig: missing dataRoot throws ConfigError (0.242893ms)
|
||||
✔ loadConfig: relative dataRoot throws ConfigError (0.228208ms)
|
||||
✔ loadConfig: valid config returns dataRoot (0.67885ms)
|
||||
✔ findNewestSession: picks the newest by mtime among two files (0.456236ms)
|
||||
✔ findNewestSession: finds files in nested subdirectories (0.293162ms)
|
||||
✔ findNewestSession: returns null for a missing dir (0.133449ms)
|
||||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.655969ms)
|
||||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.28215ms)
|
||||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.518602ms)
|
||||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.153363ms)
|
||||
✔ deriveState: full state table (0.191897ms)
|
||||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.329398ms)
|
||||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.287165ms)
|
||||
✔ rule: a finished ordinary turn is idle, even if it says your move (0.292841ms)
|
||||
✔ task: the first user message of the session, from text blocks (0.251449ms)
|
||||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.262458ms)
|
||||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.284624ms)
|
||||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.413839ms)
|
||||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.540086ms)
|
||||
✔ scan: the written record carries task, workspace and activeProject (0.567503ms)
|
||||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.593814ms)
|
||||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.389377ms)
|
||||
✔ 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 (0.998733ms)
|
||||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.432052ms)
|
||||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.152121ms)
|
||||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.725057ms)
|
||||
✔ 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.325035ms)
|
||||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (0.688081ms)
|
||||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.259151ms)
|
||||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.554828ms)
|
||||
✔ scanAgent: ageSeconds is computed from the injected now (0.26272ms)
|
||||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.170915ms)
|
||||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.347837ms)
|
||||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.36176ms)
|
||||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.869696ms)
|
||||
✔ scan: relative boardDir throws ConfigError (0.092617ms)
|
||||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (59.687655ms)
|
||||
✔ CLI: missing config exits 2 with a refused: message (56.624867ms)
|
||||
✔ CLI: unknown command exits 2 (47.940261ms)
|
||||
✔ CLI: unknown --liveness value exits 2 (51.16327ms)
|
||||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.176108ms)
|
||||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.077922ms)
|
||||
✔ tmuxIsAlive: a pane running pi is alive (0.174784ms)
|
||||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.08773ms)
|
||||
✔ tmuxIsAlive: no such tmux session is not alive (0.05117ms)
|
||||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.067521ms)
|
||||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.099285ms)
|
||||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.083031ms)
|
||||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.078034ms)
|
||||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.088586ms)
|
||||
✔ loadSeen: missing file returns {} (0.145413ms)
|
||||
✔ loadSeen: invalid JSON throws ConfigError (0.181617ms)
|
||||
✔ loadSeen: a JSON array throws ConfigError (0.149855ms)
|
||||
✔ loadSeen: a non-string value throws ConfigError (0.155826ms)
|
||||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.347995ms)
|
||||
✔ markSeen: seen false deletes the key (0.259578ms)
|
||||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.201353ms)
|
||||
✔ markSeen: project containing '/' throws ConfigError (0.118416ms)
|
||||
✔ markSeen: non-boolean seen throws ConfigError (0.113757ms)
|
||||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.265895ms)
|
||||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.234309ms)
|
||||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.220257ms)
|
||||
✔ scanAgent: an error-state session with a matching mark is seen (0.238872ms)
|
||||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.491888ms)
|
||||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.174158ms)
|
||||
✔ 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.650523ms)
|
||||
✔ 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.575227ms)
|
||||
✔ 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.931945ms)
|
||||
✔ isLoopbackHost: recognizes loopback hosts (1.236351ms)
|
||||
✔ isLoopbackHost: rejects non-loopback hosts (4.605657ms)
|
||||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.139722ms)
|
||||
✔ startServer: serves page, healthz, and a rescanning /api/board (33.590332ms)
|
||||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (6.066904ms)
|
||||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (3.324998ms)
|
||||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (59.308293ms)
|
||||
✔ CLI: serve rejects a non-numeric --port with exit 2 (55.522575ms)
|
||||
✔ CLI: scan still works after the async cli refactor (53.850787ms)
|
||||
✔ CLI: live serve prints its URL and answers /healthz (61.200639ms)
|
||||
✔ page.html: esc() escapes every HTML-significant character (0.675095ms)
|
||||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (8.234269ms)
|
||||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (2.545656ms)
|
||||
✔ POST /api/seen with invalid JSON returns 400 (3.060052ms)
|
||||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (2.197926ms)
|
||||
✔ POST /api/seen with a missing agent returns 400 (1.398132ms)
|
||||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.736396ms)
|
||||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (53.499758ms)
|
||||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.308873ms)
|
||||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.265262ms)
|
||||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.166295ms)
|
||||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.126278ms)
|
||||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.310662ms)
|
||||
✔ 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.56578ms)
|
||||
✔ 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 (30.870217ms)
|
||||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (26.792986ms)
|
||||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (25.851421ms)
|
||||
✔ 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.635697ms)
|
||||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.589091ms)
|
||||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.15599ms)
|
||||
✔ 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 (0.54229ms)
|
||||
✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.532864ms)
|
||||
✔ 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.265191ms)
|
||||
✔ 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 (7.573961ms)
|
||||
✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (38.456176ms)
|
||||
✔ 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.733812ms)
|
||||
✔ every refusal code the reader can raise has an HTTP status (0.931204ms)
|
||||
✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (46.821522ms)
|
||||
ℹ tests 124
|
||||
ℹ suites 0
|
||||
ℹ pass 124
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 649.810218
|
||||
@@ -0,0 +1,169 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (209.751905ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (38.333254ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (21.625618ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (402.771722ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (396.446509ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (240.690278ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (226.460251ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (234.896642ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.398274ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11455.374433ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (29497.79063ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (261.396667ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (147.713723ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (377.884533ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (275.319295ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (339.355963ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (103.909867ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (168.85289ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (257.112717ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (719.077212ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (180.770662ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (92.531202ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (67.979598ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.119897ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.141711ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.340769ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2584.162147ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (492.008614ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2664.877961ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4972.784362ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2624.532286ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2674.914288ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2505.408123ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5120.83543ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (773.580048ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (731.302187ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (478.288361ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (996.568511ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (426.151347ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (558.492406ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3283.844693ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (41.467532ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (349.460793ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (277.967738ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.817843ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (204.0552ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (226.569652ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (200.639765ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (282.434891ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (206.997328ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.451299ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (183.784874ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (329.079159ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (245.587631ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (218.337437ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (274.804399ms)
|
||||
✔ 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 (282.915848ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.545942ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (256.128477ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (234.961382ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (343.238488ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (246.328666ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.506634ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.2927ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.359291ms)
|
||||
✔ 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.530604ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.313817ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.280818ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.424211ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.397178ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.119418ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (234.698348ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (249.331105ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (318.777107ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (803.326944ms)
|
||||
✔ H4: self-takeover is refused (237.029747ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (492.168032ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1309.455766ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (329.117142ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (678.52757ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (351.993748ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (217.254745ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (227.625564ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (631.813088ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (334.100158ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (345.524957ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1328.037967ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (605.823799ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (921.492468ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (230.650527ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (482.41378ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.719789ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (750.256831ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (672.238142ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (880.196859ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3153.535094ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (711.807279ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.139723ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.86508ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.660465ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.466484ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.487751ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.036768ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.859256ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.576809ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.759899ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.209432ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (8.693023ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.576323ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.996735ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.966028ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.017387ms)
|
||||
✔ F9: registrations never add or redirect a root (1.943997ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.50071ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.83125ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.862495ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.448358ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.302207ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.741404ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (581.485803ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.298589ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.867231ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.492391ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.057707ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.989865ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (775.588337ms)
|
||||
✔ the engine pin holds for the installed package (1.82016ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (303.853561ms)
|
||||
✔ 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) (549.826273ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (266.029933ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (507.922278ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (438.769512ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (240.225642ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.028887ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (390.161641ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (225.42443ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (320.411831ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (663.311464ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1925.859529ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (219.287665ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (325.691915ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (212.240769ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (278.3767ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (526.1467ms)
|
||||
✔ N10: fake conformance (32.836031ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (764.760337ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (380.934245ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (382.303541ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (626.979744ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (493.927018ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (385.580455ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (210.180313ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (584.079529ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (823.131625ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (883.020548ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (624.260054ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (279.042334ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (268.120106ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (262.602992ms)
|
||||
✔ 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 (332.99312ms)
|
||||
✔ 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.330557ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 161
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 47640.577367
|
||||
@@ -0,0 +1,31 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3030.316712ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3098.145116ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4710.171327ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (251.312525ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (91.580386ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (64.76072ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2852.574308ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (96.386424ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.134961ms)
|
||||
✔ serve refuses a --business value that is not a business id (193.493434ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3016.442078ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1770.01472ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1138.252882ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (813.139622ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2929.646577ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53584.87674ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2994.618722ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22249.862961ms)
|
||||
✔ loopback host and board origin fail closed (8.473866ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (115.009552ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (115.397812ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1711.384212ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 22
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54018.332686
|
||||
@@ -0,0 +1,14 @@
|
||||
1 stacked Ctrl-T, Ctrl-O, hi Enter: sent []; composer ""; status "takeover: transferred"
|
||||
order: observe -> takeover start -> takeover end -> observe
|
||||
2 takeover throws, via input(): sent []; composer "hi"; status "not admitted: controller"
|
||||
order: observe -> takeover start -> takeover threw -> call 0 resolved -> call 1 resolved
|
||||
3 takeover throws, via key(): sent []; composer "hi"; status "not admitted: controller"
|
||||
order: observe -> takeover start -> takeover threw -> call 0 threw not connected -> call 1 resolved
|
||||
4 earlier Ctrl-G throws: sent ["hi"]; composer ""; status "prompt: admitted"
|
||||
order: observe -> interrupt start -> interrupt end -> call 0 threw interrupt broke -> takeover start -> takeover end -> prompt hi start -> prompt hi end -> call 1 resolved -> call 2 resolved
|
||||
5 paste split across held chunks: sent ["x\ry"]; composer ""; status "prompt: admitted"
|
||||
order: observe -> takeover start -> takeover end -> prompt x␍y start -> prompt x␍y end
|
||||
6 input while the drained prompt runs: sent ["a","b"]; composer ""; status "prompt: admitted"
|
||||
order: observe -> takeover start -> takeover end -> prompt a start -> prompt a end -> prompt b start -> prompt b end
|
||||
7 Ctrl-C behind a takeover that never answers: quit called after 500 ms: false; held "\u0003"
|
||||
8 Ctrl-C behind a Ctrl-G that never answers (no hold): quit called after 500 ms: false; held null
|
||||
@@ -0,0 +1,14 @@
|
||||
1 stacked Ctrl-T, Ctrl-O, hi Enter: sent ["hi"]; composer ""; status "prompt: admitted"
|
||||
order: observe -> takeover start -> takeover end -> observe -> prompt hi start -> prompt hi end
|
||||
2 takeover throws, via input(): sent []; composer "hi"; status "not admitted: controller"
|
||||
order: observe -> takeover start -> takeover threw -> call 0 resolved -> call 1 resolved
|
||||
3 takeover throws, via key(): sent []; composer "hi"; status "not admitted: controller"
|
||||
order: observe -> takeover start -> takeover threw -> call 0 threw not connected -> call 1 resolved
|
||||
4 earlier Ctrl-G throws: sent ["hi"]; composer ""; status "prompt: admitted"
|
||||
order: observe -> interrupt start -> interrupt end -> call 0 threw interrupt broke -> takeover start -> takeover end -> prompt hi start -> prompt hi end -> call 1 resolved -> call 2 resolved
|
||||
5 paste split across held chunks: sent ["x\ry"]; composer ""; status "prompt: admitted"
|
||||
order: observe -> takeover start -> takeover end -> prompt x␍y start -> prompt x␍y end
|
||||
6 input while the drained prompt runs: sent ["a","b"]; composer ""; status "prompt: admitted"
|
||||
order: observe -> takeover start -> takeover end -> prompt a start -> prompt a end -> prompt b start -> prompt b end
|
||||
7 Ctrl-C behind a takeover that never answers: quit called after 500 ms: false; held "\u0003"
|
||||
8 Ctrl-C behind a Ctrl-G that never answers (no hold): quit called after 500 ms: false; held null
|
||||
@@ -0,0 +1,6 @@
|
||||
separate chunks: sent ["hi"]; composer ""; status "prompt: admitted"
|
||||
order: interrupt start -> interrupt end -> takeover start -> takeover end -> prompt hi
|
||||
one chunk after Ctrl-G: sent ["hi"]; composer ""; status "prompt: admitted"
|
||||
order: interrupt start -> interrupt end -> takeover start -> takeover end -> prompt hi
|
||||
control: no earlier action: sent ["hi"]; composer ""; status "prompt: admitted"
|
||||
order: takeover start -> takeover end -> prompt hi
|
||||
@@ -0,0 +1,4 @@
|
||||
== base
|
||||
Ctrl-C behind a hung Ctrl-G: quit called after 500 ms: false
|
||||
== wt
|
||||
Ctrl-C behind a hung Ctrl-G: quit called after 500 ms: false
|
||||
@@ -0,0 +1,2 @@
|
||||
candidate: sent ["hi","yo"]; held null; quit true
|
||||
Mr-no-recompute: sent []; held "hi\ryo\r\u0003"; quit false
|
||||
@@ -0,0 +1,5 @@
|
||||
Mm: humanCall -> outcome-unknown after 705 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 22 ℹ fail 0 54s
|
||||
node-conversation rc=0 ℹ pass 161 ℹ 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 6s
|
||||
test-config rc=0 OK failed validation modified nothing selftest: 24 passed, 0 failed 1s
|
||||
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 2s
|
||||
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/r40c/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 '86b22cc2e6db3920cecab77e51b47bf9094a6b48'.
|
||||
|
||||
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 '86b22cc2e6db3920cecab77e51b47bf9094a6b48'.
|
||||
|
||||
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: 1794402 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/r40c/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,85 @@
|
||||
// Round 3 edges of the Ctrl-T/Ctrl-O hold: stacked holds, a takeover that
|
||||
// throws (input() and key()), an earlier chunk that throws, a paste split
|
||||
// across held chunks, input arriving while the drained prompt still runs, and
|
||||
// Ctrl-C behind a takeover that never answers (with the same Ctrl-C behind a
|
||||
// hung Ctrl-G for comparison, which has no hold). Imports from the review
|
||||
// worktree by absolute path (WT).
|
||||
const WT = process.env.WT;
|
||||
const { Terminal } = await import(`${WT}/packages/conversation/src/terminal.mjs`);
|
||||
const tick = (ms = 10) => new Promise((r) => setTimeout(r, ms));
|
||||
function gate() {
|
||||
let open;
|
||||
const p = new Promise((r) => (open = r));
|
||||
return { p, open };
|
||||
}
|
||||
function stub({ takeoverThrows = false, interruptThrows = false, promptGate = null } = {}) {
|
||||
const sent = [], log = [];
|
||||
let listener = () => {};
|
||||
const g = { int: gate(), tk: gate(), obs: gate() };
|
||||
const s = {
|
||||
closed: false, connection: { id: "conn-1" }, binding: { state: "active", controllerConnection: "conn-2" }, isController: false,
|
||||
on: (fn) => (listener = fn),
|
||||
observe: async () => { log.push("observe"); return { outcome: "refused:channel", refusal: "channel" }; },
|
||||
prompt: async (t) => {
|
||||
log.push(`prompt ${t} start`);
|
||||
if (promptGate && t === "a") await promptGate.p;
|
||||
log.push(`prompt ${t} end`); sent.push(t);
|
||||
return { outcome: "admitted", refusal: null, receipt: { id: `r-${t}`, state: "admitted" } };
|
||||
},
|
||||
interrupt: async () => {
|
||||
log.push("interrupt start"); await g.int.p; log.push("interrupt end");
|
||||
if (interruptThrows) throw new Error("interrupt broke");
|
||||
return { outcome: "refused:controller", refusal: "controller" };
|
||||
},
|
||||
takeover: async () => {
|
||||
log.push("takeover start"); await g.tk.p;
|
||||
if (takeoverThrows) { log.push("takeover threw"); throw new Error("not connected"); }
|
||||
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, g };
|
||||
}
|
||||
function report(name, t, x, extra = "") {
|
||||
console.log(`${name}: sent ${JSON.stringify(x.sent)}; composer ${JSON.stringify(t.composer)}; status ${JSON.stringify(t.status)}${extra}`);
|
||||
console.log(` order: ${x.log.join(" -> ")}`);
|
||||
}
|
||||
|
||||
{ // 1. Ctrl-T pending, then Ctrl-O, then "hi" Enter, as separate chunks.
|
||||
const x = stub(); const t = new Terminal({ client: x.s });
|
||||
const done = ["\x14", "\x0f", "hi\r"].map((c) => t.key(c).catch((e) => x.log.push(`threw ${e.message}`)));
|
||||
await tick(); x.g.tk.open(); await Promise.all(done); await tick();
|
||||
report("1 stacked Ctrl-T, Ctrl-O, hi Enter", t, x);
|
||||
}
|
||||
for (const via of ["input", "key"]) { // 2, 3. The takeover throws.
|
||||
const x = stub({ takeoverThrows: true }); const t = new Terminal({ client: x.s });
|
||||
const done = ["\x14", "hi\r"].map((c, i) => t[via](c).then(() => x.log.push(`call ${i} resolved`), (e) => x.log.push(`call ${i} threw ${e.message}`)));
|
||||
await tick(); x.g.tk.open(); await Promise.all(done);
|
||||
report(`${via === "input" ? 2 : 3} takeover throws, via ${via}()`, t, x);
|
||||
}
|
||||
{ // 4. An earlier chunk's Ctrl-G throws (key()), then Ctrl-T and hi Enter.
|
||||
const x = stub({ interruptThrows: true }); const t = new Terminal({ client: x.s });
|
||||
const done = ["\x07", "\x14", "hi\r"].map((c, i) => t.key(c).then(() => x.log.push(`call ${i} resolved`), (e) => x.log.push(`call ${i} threw ${e.message}`)));
|
||||
await tick(); x.g.int.open(); await tick(); x.g.tk.open(); await Promise.all(done);
|
||||
report("4 earlier Ctrl-G throws", t, x);
|
||||
}
|
||||
{ // 5. A paste split across held chunks, the start marker cut in two.
|
||||
const x = stub(); const t = new Terminal({ client: x.s });
|
||||
const done = ["\x14", "\x1b[20", "0~x\ry\x1b[201~", "\r"].map((c) => t.key(c));
|
||||
await tick(); x.g.tk.open(); await Promise.all(done);
|
||||
report("5 paste split across held chunks", t, x);
|
||||
}
|
||||
{ // 6. "b" Enter arrives while the drained "a" prompt still runs.
|
||||
const pg = gate(); const x = stub({ promptGate: pg }); const t = new Terminal({ client: x.s });
|
||||
const d1 = ["\x14", "a\r"].map((c) => t.key(c));
|
||||
await tick(); x.g.tk.open(); await tick();
|
||||
const d2 = t.key("b\r"); await tick(); pg.open(); await Promise.all([...d1, d2]);
|
||||
report("6 input while the drained prompt runs", t, x);
|
||||
}
|
||||
for (const [name, first] of [["7 Ctrl-C behind a takeover that never answers", "\x14"], ["8 Ctrl-C behind a Ctrl-G that never answers (no hold)", "\x07"]]) {
|
||||
const x = stub(); let quit = false; const t = new Terminal({ client: x.s, onQuit: () => (quit = true) });
|
||||
t.input(first); t.input("\x03");
|
||||
await tick(500);
|
||||
console.log(`${name}: quit called after 500 ms: ${quit}; held ${JSON.stringify(t.held)}`);
|
||||
}
|
||||
process.exit(0);
|
||||
@@ -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,10 @@
|
||||
// Ctrl-C behind a Ctrl-G that never answers, against any terminal.mjs (WT):
|
||||
// run against the base to show the wait predates the hold.
|
||||
const { Terminal } = await import(`${process.env.WT}/packages/conversation/src/terminal.mjs`);
|
||||
let quit = false;
|
||||
const s = { closed: false, connection: { id: "c1" }, binding: { state: "active", controllerConnection: "c2" }, isController: false, on: () => {}, observe: async () => ({ outcome: "refused:channel", refusal: "channel" }), interrupt: () => new Promise(() => {}) };
|
||||
const t = new Terminal({ client: s, onQuit: () => (quit = true) });
|
||||
(t.input ?? t.key).call(t, "\x07"); (t.input ?? t.key).call(t, "\x03");
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
console.log(`Ctrl-C behind a hung Ctrl-G: quit called after 500 ms: ${quit}`);
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,28 @@
|
||||
// After a stacked hold (Ctrl-T, then Ctrl-O in the held text, then "hi"
|
||||
// Enter), does later input still run? Types "yo" Enter and Ctrl-C afterwards
|
||||
// and reports what was sent, `held`, and whether quit was called. Run against
|
||||
// the candidate and under Mr-no-recompute. Imports from the review worktree
|
||||
// by absolute path (WT).
|
||||
const WT = process.env.WT;
|
||||
const { Terminal } = await import(`${WT}/packages/conversation/src/terminal.mjs`);
|
||||
const tick = (ms = 10) => new Promise((r) => setTimeout(r, ms));
|
||||
const sent = [];
|
||||
let listener = () => {};
|
||||
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) => (sent.push(t), { outcome: "admitted", refusal: null, receipt: { id: `r-${t}`, state: "admitted" } }),
|
||||
takeover: async () => {
|
||||
s.isController = true; s.binding = { state: "active", controllerConnection: "conn-1" }; listener({ type: "push", kind: "binding" });
|
||||
return { outcome: "transferred", refusal: null };
|
||||
},
|
||||
};
|
||||
let quit = false;
|
||||
const t = new Terminal({ client: s, onQuit: () => (quit = true) });
|
||||
for (const c of ["\x14", "\x0f", "hi\r"]) t.input(c);
|
||||
await tick(50);
|
||||
for (const c of ["yo\r", "\x03"]) t.input(c);
|
||||
await tick(50);
|
||||
console.log(`sent ${JSON.stringify(sent)}; held ${JSON.stringify(t.held)}; quit ${quit}`);
|
||||
process.exit(0);
|
||||
@@ -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,191 @@
|
||||
# Row 40, slice 1 S5 (Console bus views and conversation hardening), round 3 review (Darkwing)
|
||||
|
||||
Issue #1522, request comment 27007, notes comment 27008, queue rev 257.
|
||||
Candidate: 27 files, uncommitted in the main checkout, listed in
|
||||
`r3/files.txt`. Manifest `agents/dewey/work/queue-40/candidate-manifest-r3.sha256`
|
||||
(copy in `r3/`), sha256
|
||||
`d0aa0ded00e7d396ff11c651277e1d20b697c526fa831b1d409a665095f1680b`.
|
||||
Seven files changed since round 2: `terminal.mjs`, `flows.test.mjs`,
|
||||
`webui/tests/bus.test.mjs`, both READMEs, `DEFERRED.md` and
|
||||
`s5-mutants.md`. Round 2: my comment 27001 (changes, T1), Filbert's
|
||||
comment 27005. Filbert approved round 3 in comment 27011 (rev 260).
|
||||
|
||||
Verdict: **approve**, comment 27013. T1 is fixed and tested, and
|
||||
Filbert's N4 holds. My round 2 notes are all dealt with. One new mutant
|
||||
survives (Mr). The code it changes is right, but nothing tests a hold
|
||||
inside held text, and under Mr the terminal ignores all later input,
|
||||
Ctrl-C included. That is note 1, with a test to add. No round 2 copy of
|
||||
the candidate survives in my scratch, so I reviewed the seven files
|
||||
against the base, not as an interdiff.
|
||||
|
||||
## Method
|
||||
|
||||
- A detached worktree at `86b22cc2`. I extracted the 27 candidate files
|
||||
over it from a tar snapshot of the shared checkout 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
|
||||
(`r3/mut/manifest-after.txt`).
|
||||
- I read `terminal.mjs` in full, the diffs of the other six files against
|
||||
the base, the new flows and bus tests, Dewey's mutant evidence and
|
||||
Filbert's comment 27011. For the WebUI README's `--business` claim I
|
||||
read `packages/cli/src/cli.mjs:75-104`.
|
||||
- Probes in `r3/probe/` import from my scratch worktree by absolute path
|
||||
(`WT`), so they won't run as committed without setting it.
|
||||
`quit-base.mjs` also ran against a second worktree at the base with no
|
||||
candidate.
|
||||
- 22 mutants (`r3/mut/mutate.py`, `run.sh`, `all.sh`): round 2's 16, plus
|
||||
six on the new `#feed`/`#run` code. Each ran its package's node suite,
|
||||
then the file was restored from a backup copy.
|
||||
|
||||
Node is v26.8.1, `TMPDIR=~/darkwing-scratch/tmp`. `gate.sh` set
|
||||
`DOCKER_HOST=unix:///nonexistent.sock`.
|
||||
|
||||
## Suites
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| webui | 22/22 |
|
||||
| conversation | 161/161 |
|
||||
| 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-docker.txt`) |
|
||||
| test-task | 26/2: "user recall run succeeds" and "recalled user name" |
|
||||
|
||||
The two test-task failures are the live recall, as in round 2: a real Pi
|
||||
worker in Docker against the zai provider, which would be a paid call.
|
||||
I didn't make it.
|
||||
|
||||
## T1: fixed
|
||||
|
||||
`#feed` (`terminal.mjs:121-130`) parses the chunk and records `holds`,
|
||||
whether this parse set `held`. `#run` (lines 143-161) drains `held` only
|
||||
when `holds` is set, and recomputes it after each parse of the drained
|
||||
text. An earlier chunk's run stops after its own actions. A chunk that
|
||||
arrives while `held` is set is appended and never parsed by itself.
|
||||
|
||||
`probe/hold-order.mjs`, unchanged from round 2 (`out/probe-hold-order.txt`):
|
||||
all three cases send `hi` with status "prompt: admitted", and the two with
|
||||
a Ctrl-G run interrupt, takeover, then the prompt. The new flows test has
|
||||
the probe's shape over three chunkings, asserts that `held` is still set
|
||||
and nothing sent before the takeover opens, and checks the call order.
|
||||
Mq, which drains `held` from any run as round 2 did, fails it.
|
||||
|
||||
The header (`terminal.mjs:17-19`) and the DEFERRED line now say what the
|
||||
code does.
|
||||
|
||||
## N4 and the per-action catch
|
||||
|
||||
`#run` catches each action on its own. With `report` (the `input()` path)
|
||||
it reports the error when it happens, so a later status stands; without
|
||||
it (`key()`) the first error is rethrown at the end. A held chunk returns
|
||||
`queue.catch(() => {})`, so its `key()` doesn't carry the holder's error.
|
||||
|
||||
`probe/hold-edges.mjs` (`out/probe-hold-edges.txt`), each case read as
|
||||
one key at a time:
|
||||
|
||||
| Case | Result |
|
||||
|---|---|
|
||||
| Ctrl-T, Ctrl-O, `hi` Enter as three chunks | `hi` sent after both |
|
||||
| The takeover throws, via `input()` | nothing sent, `hi` left in the composer, status "not admitted: controller", both calls resolve |
|
||||
| The same via `key()` | call 0 rejects "not connected", call 1 resolves |
|
||||
| An earlier Ctrl-G throws | the takeover still runs and `hi` is sent |
|
||||
| A paste split across held chunks, its start marker cut | `x\ry` sent as one message |
|
||||
| `b` Enter typed while the drained `a` prompt runs | `a` then `b`, in order |
|
||||
|
||||
## Round 2 notes
|
||||
|
||||
1. DEFERRED keeps N2 and moves F2 to done citing T1, which is now true.
|
||||
It is Sage's file, so this is Dewey's proposal for Sage to take or
|
||||
leave.
|
||||
2. The conversation README says the seal leaves context files on and that
|
||||
whoever sets `engine.cwd` chooses them.
|
||||
3. The WebUI README says the copied `mosaic decide` has no `--business`.
|
||||
`businessFor` (`cli.mjs:75-79`) takes the running host's business, and
|
||||
`findDecision` looks only in that business's inbox, so a command copied
|
||||
from a Console for another business finds nothing and refuses, or
|
||||
there's no host. The README says that.
|
||||
4. Mm and Mp now each have a test that fails under the mutant (below).
|
||||
|
||||
## 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: N1, F2, T1, N4 |
|
||||
| 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 | killed | humanCall kills a transport that runs past its timeout |
|
||||
| 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 | killed | serve refuses a --business value that is not a business id |
|
||||
| Mq-drain-any | `#run` drains `held` whoever set it (round 2) | killed | terminal: T1 |
|
||||
| Mr-no-recompute | `holds` set false after a drained parse | **survived** | |
|
||||
| Ms-no-try | no per-action catch in `#run` | killed | terminal: N1; a throw still releases held input; N4 |
|
||||
| Mt-held-rethrow | a held chunk returns `this.queue` uncaught | killed | terminal: N4 |
|
||||
| Mu-no-report | `input()` errors held to the end, not reported | killed | terminal: N1 |
|
||||
| Mv-feed-holds-true | every run acts as the one that set `held` | killed | seven tests, among them E1, S4, S5 and T1 |
|
||||
|
||||
Twenty of 22 killed. Mk is as in round 2: the broker refuses an option
|
||||
key that fails its `id()` rule, so the browser check can't be reached
|
||||
through a real broker.
|
||||
|
||||
## Notes (not blocking)
|
||||
|
||||
1. **Mr survives, and its failure locks the terminal.** When drained text
|
||||
holds again (a Ctrl-O or second Ctrl-T inside held input), `#run` must
|
||||
recompute `holds` (line 158) to keep draining. Under Mr it stops, and
|
||||
`held` stays set. Nothing clears it, so every later chunk is appended
|
||||
to `held` and never parsed. `probe/stacked-later.mjs`
|
||||
(`out/probe-stacked-later.txt`) types Ctrl-T, Ctrl-O, `hi` Enter,
|
||||
then `yo` Enter and Ctrl-C:
|
||||
|
||||
```
|
||||
candidate: sent ["hi","yo"]; held null; quit true
|
||||
Mr-no-recompute: sent []; held "hi\ryo\r\u0003"; quit false
|
||||
```
|
||||
|
||||
The candidate is right; Filbert's probe of `\x14a\x14` agrees. But a
|
||||
one-line regression here would leave the operator unable to type or
|
||||
quit, and the suite stays green. A flows test with the probe's shape
|
||||
(a hold inside held text, then later input) would catch it. I'd add it
|
||||
before landing or as the first follow-up.
|
||||
2. Ctrl-C and Ctrl-D wait behind any pending action. `client.request()`
|
||||
has no timeout, and every action, quit included, chains on
|
||||
`this.queue`. Raw mode means the terminal gets no SIGINT. So behind a
|
||||
takeover or Ctrl-G the controller never answers, quit doesn't run
|
||||
(`hold-edges` cases 7 and 8). `probe/quit-base.mjs` gives the same
|
||||
result on the base, so this predates the hold. A quit path that skips
|
||||
the queue, or a request timeout, would fix it. That belongs with the
|
||||
I3 entry point, not this row.
|
||||
3. Gate E isn't run yet and nothing outside the tests constructs a
|
||||
Controller, as in round 2. This approval doesn't cover it.
|
||||
|
||||
## Files
|
||||
|
||||
- `review-r3.md`: this review.
|
||||
- `r3/files.txt`, `r3/candidate-manifest-r3.sha256`: the candidate as reviewed.
|
||||
- `r3/gate.sh`, `r3/out/`: the suite runs, `out/summary.txt` first.
|
||||
- `r3/probe/hold-order.mjs`, `out/probe-hold-order.txt`: T1.
|
||||
- `r3/probe/hold-edges.mjs`, `out/probe-hold-edges.txt`: the hold cases
|
||||
above; `out/probe-hold-edges-Mr.txt` is the same run under Mr.
|
||||
- `r3/probe/stacked-later.mjs`, `out/probe-stacked-later.txt`: note 1.
|
||||
- `r3/probe/quit-base.mjs`, `out/probe-quit-base.txt`: note 2, base and
|
||||
candidate.
|
||||
- `r3/probe/survivors.mjs`, `out/probe-survivors.txt`: Mm and Mp by hand,
|
||||
kept from round 2.
|
||||
- `r3/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