From 67630cc41ff8f4d76471adc1ab490a2013d3400c Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Fri, 9 Oct 2026 21:59:58 -0500 Subject: [PATCH] docs(review): row 40 round 3 review packet, approve (darkwing) Co-Authored-By: Claude Opus 5.5 --- .../r3/candidate-manifest-r3.sha256 | 27 ++ agents/darkwing/work/s40-review/r3/files.txt | 27 ++ agents/darkwing/work/s40-review/r3/gate.sh | 13 + .../r3/mut/Ma-no-approve-conversation.txt | 217 +++++++++++++ .../r3/mut/Mb-seal-command-conversation.txt | 185 +++++++++++ .../r3/mut/Mc-engine-keys-conversation.txt | 207 ++++++++++++ .../r3/mut/Md-env-inherit-conversation.txt | 208 ++++++++++++ .../r3/mut/Me-escalating-conversation.txt | 191 +++++++++++ .../r3/mut/Mf-queue-catch-conversation.txt | 185 +++++++++++ .../r3/mut/Mg-no-hold-conversation.txt | 249 +++++++++++++++ .../r3/mut/Mh-manager-env-conversation.txt | 182 +++++++++++ .../s40-review/r3/mut/Mi-bus-post-webui.txt | 51 +++ .../s40-review/r3/mut/Mj-subject-webui.txt | 56 ++++ .../s40-review/r3/mut/Mk-copy-safe-webui.txt | 31 ++ .../r3/mut/Ml-option-inert-webui.txt | 52 +++ .../r3/mut/Mm-timeout-kill-webui.txt | 47 +++ .../s40-review/r3/mut/Mn-max-bytes-webui.txt | 54 ++++ .../s40-review/r3/mut/Mo-status-webui.txt | 66 ++++ .../r3/mut/Mp-business-id-webui.txt | 54 ++++ .../r3/mut/Mq-drain-any-conversation.txt | 185 +++++++++++ .../r3/mut/Mr-no-recompute-conversation.txt | 169 ++++++++++ .../r3/mut/Ms-no-try-conversation.txt | 230 +++++++++++++ .../r3/mut/Mt-held-rethrow-conversation.txt | 185 +++++++++++ .../r3/mut/Mu-no-report-conversation.txt | 190 +++++++++++ .../mut/Mv-feed-holds-true-conversation.txt | 302 ++++++++++++++++++ agents/darkwing/work/s40-review/r3/mut/all.sh | 6 + .../work/s40-review/r3/mut/manifest-after.txt | 1 + .../darkwing/work/s40-review/r3/mut/mutate.py | 33 ++ agents/darkwing/work/s40-review/r3/mut/run.sh | 14 + .../work/s40-review/r3/mut/summary.txt | 23 ++ .../s40-review/r3/out/node-control-board.txt | 132 ++++++++ .../s40-review/r3/out/node-conversation.txt | 169 ++++++++++ .../work/s40-review/r3/out/node-webui.txt | 31 ++ .../s40-review/r3/out/probe-hold-edges-Mr.txt | 14 + .../s40-review/r3/out/probe-hold-edges.txt | 14 + .../s40-review/r3/out/probe-hold-order.txt | 6 + .../s40-review/r3/out/probe-quit-base.txt | 4 + .../s40-review/r3/out/probe-stacked-later.txt | 2 + .../s40-review/r3/out/probe-survivors.txt | 5 + .../work/s40-review/r3/out/summary.txt | 13 + .../work/s40-review/r3/out/test-auth.txt | 17 + .../work/s40-review/r3/out/test-conductor.txt | 55 ++++ .../work/s40-review/r3/out/test-config.txt | 26 ++ .../work/s40-review/r3/out/test-discord.txt | 70 ++++ .../r3/out/test-extension-package.txt | 21 ++ .../s40-review/r3/out/test-foundation.txt | 53 +++ .../work/s40-review/r3/out/test-queue.txt | 35 ++ .../s40-review/r3/out/test-release-docker.txt | 16 + .../work/s40-review/r3/out/test-release.txt | 7 + .../work/s40-review/r3/out/test-task.txt | 33 ++ .../work/s40-review/r3/probe/hold-edges.mjs | 85 +++++ .../work/s40-review/r3/probe/hold-order.mjs | 35 ++ .../work/s40-review/r3/probe/quit-base.mjs | 10 + .../s40-review/r3/probe/stacked-later.mjs | 28 ++ .../work/s40-review/r3/probe/survivors.mjs | 26 ++ agents/darkwing/work/s40-review/review-r3.md | 191 +++++++++++ 56 files changed, 4538 insertions(+) create mode 100644 agents/darkwing/work/s40-review/r3/candidate-manifest-r3.sha256 create mode 100644 agents/darkwing/work/s40-review/r3/files.txt create mode 100755 agents/darkwing/work/s40-review/r3/gate.sh create mode 100644 agents/darkwing/work/s40-review/r3/mut/Ma-no-approve-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mb-seal-command-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mc-engine-keys-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Md-env-inherit-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Me-escalating-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mf-queue-catch-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mg-no-hold-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mh-manager-env-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mi-bus-post-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mj-subject-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mk-copy-safe-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Ml-option-inert-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mm-timeout-kill-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mn-max-bytes-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mo-status-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mp-business-id-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mq-drain-any-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mr-no-recompute-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Ms-no-try-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mt-held-rethrow-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mu-no-report-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/Mv-feed-holds-true-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/all.sh create mode 100644 agents/darkwing/work/s40-review/r3/mut/manifest-after.txt create mode 100644 agents/darkwing/work/s40-review/r3/mut/mutate.py create mode 100644 agents/darkwing/work/s40-review/r3/mut/run.sh create mode 100644 agents/darkwing/work/s40-review/r3/mut/summary.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/node-control-board.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/node-conversation.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/node-webui.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/probe-hold-edges-Mr.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/probe-hold-edges.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/probe-hold-order.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/probe-quit-base.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/probe-stacked-later.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/probe-survivors.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/summary.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-auth.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-conductor.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-config.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-discord.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-extension-package.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-foundation.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-queue.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-release-docker.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-release.txt create mode 100644 agents/darkwing/work/s40-review/r3/out/test-task.txt create mode 100644 agents/darkwing/work/s40-review/r3/probe/hold-edges.mjs create mode 100644 agents/darkwing/work/s40-review/r3/probe/hold-order.mjs create mode 100644 agents/darkwing/work/s40-review/r3/probe/quit-base.mjs create mode 100644 agents/darkwing/work/s40-review/r3/probe/stacked-later.mjs create mode 100644 agents/darkwing/work/s40-review/r3/probe/survivors.mjs create mode 100644 agents/darkwing/work/s40-review/review-r3.md diff --git a/agents/darkwing/work/s40-review/r3/candidate-manifest-r3.sha256 b/agents/darkwing/work/s40-review/r3/candidate-manifest-r3.sha256 new file mode 100644 index 00000000..d5e7db8e --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/candidate-manifest-r3.sha256 @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/files.txt b/agents/darkwing/work/s40-review/r3/files.txt new file mode 100644 index 00000000..183a26e3 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/files.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/gate.sh b/agents/darkwing/work/s40-review/r3/gate.sh new file mode 100755 index 00000000..18eafbff --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/gate.sh @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/mut/Ma-no-approve-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Ma-no-approve-conversation.txt new file mode 100644 index 00000000..c4a2dcc3 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Ma-no-approve-conversation.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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mb-seal-command-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mb-seal-command-conversation.txt new file mode 100644 index 00000000..95b45611 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mb-seal-command-conversation.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mc-engine-keys-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mc-engine-keys-conversation.txt new file mode 100644 index 00000000..49df588c --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mc-engine-keys-conversation.txt @@ -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. (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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Md-env-inherit-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Md-env-inherit-conversation.txt new file mode 100644 index 00000000..5f927acf --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Md-env-inherit-conversation.txt @@ -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. (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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Me-escalating-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Me-escalating-conversation.txt new file mode 100644 index 00000000..426debda --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Me-escalating-conversation.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mf-queue-catch-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mf-queue-catch-conversation.txt new file mode 100644 index 00000000..c41628f0 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mf-queue-catch-conversation.txt @@ -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. (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) diff --git a/agents/darkwing/work/s40-review/r3/mut/Mg-no-hold-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mg-no-hold-conversation.txt new file mode 100644 index 00000000..0493a90c --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mg-no-hold-conversation.txt @@ -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. (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. (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. (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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mh-manager-env-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mh-manager-env-conversation.txt new file mode 100644 index 00000000..ffb21554 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mh-manager-env-conversation.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mi-bus-post-webui.txt b/agents/darkwing/work/s40-review/r3/mut/Mi-bus-post-webui.txt new file mode 100644 index 00000000..cd73fc9f --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mi-bus-post-webui.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mj-subject-webui.txt b/agents/darkwing/work/s40-review/r3/mut/Mj-subject-webui.txt new file mode 100644 index 00000000..86815888 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mj-subject-webui.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mk-copy-safe-webui.txt b/agents/darkwing/work/s40-review/r3/mut/Mk-copy-safe-webui.txt new file mode 100644 index 00000000..d2979992 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mk-copy-safe-webui.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/mut/Ml-option-inert-webui.txt b/agents/darkwing/work/s40-review/r3/mut/Ml-option-inert-webui.txt new file mode 100644 index 00000000..b6f5f288 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Ml-option-inert-webui.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mm-timeout-kill-webui.txt b/agents/darkwing/work/s40-review/r3/mut/Mm-timeout-kill-webui.txt new file mode 100644 index 00000000..7cd0a490 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mm-timeout-kill-webui.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mn-max-bytes-webui.txt b/agents/darkwing/work/s40-review/r3/mut/Mn-max-bytes-webui.txt new file mode 100644 index 00000000..39852b0c --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mn-max-bytes-webui.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mo-status-webui.txt b/agents/darkwing/work/s40-review/r3/mut/Mo-status-webui.txt new file mode 100644 index 00000000..61850457 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mo-status-webui.txt @@ -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. (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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mp-business-id-webui.txt b/agents/darkwing/work/s40-review/r3/mut/Mp-business-id-webui.txt new file mode 100644 index 00000000..f8298c3e --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mp-business-id-webui.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mq-drain-any-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mq-drain-any-conversation.txt new file mode 100644 index 00000000..80a6cd35 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mq-drain-any-conversation.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mr-no-recompute-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mr-no-recompute-conversation.txt new file mode 100644 index 00000000..d31ff6af --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mr-no-recompute-conversation.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/mut/Ms-no-try-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Ms-no-try-conversation.txt new file mode 100644 index 00000000..6747a53d --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Ms-no-try-conversation.txt @@ -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. (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. (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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mt-held-rethrow-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mt-held-rethrow-conversation.txt new file mode 100644 index 00000000..e2fdbf9d --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mt-held-rethrow-conversation.txt @@ -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. (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) diff --git a/agents/darkwing/work/s40-review/r3/mut/Mu-no-report-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mu-no-report-conversation.txt new file mode 100644 index 00000000..1b257e6d --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mu-no-report-conversation.txt @@ -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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/Mv-feed-holds-true-conversation.txt b/agents/darkwing/work/s40-review/r3/mut/Mv-feed-holds-true-conversation.txt new file mode 100644 index 00000000..c2978b55 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/Mv-feed-holds-true-conversation.txt @@ -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. (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. (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. (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. (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. (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. (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. (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' + } diff --git a/agents/darkwing/work/s40-review/r3/mut/all.sh b/agents/darkwing/work/s40-review/r3/mut/all.sh new file mode 100644 index 00000000..27292f94 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/all.sh @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/mut/manifest-after.txt b/agents/darkwing/work/s40-review/r3/mut/manifest-after.txt new file mode 100644 index 00000000..f64f5d8d --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/manifest-after.txt @@ -0,0 +1 @@ +27 diff --git a/agents/darkwing/work/s40-review/r3/mut/mutate.py b/agents/darkwing/work/s40-review/r3/mut/mutate.py new file mode 100644 index 00000000..c5695545 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/mutate.py @@ -0,0 +1,33 @@ +# mutate.py : 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) diff --git a/agents/darkwing/work/s40-review/r3/mut/run.sh b/agents/darkwing/work/s40-review/r3/mut/run.sh new file mode 100644 index 00000000..3374bc26 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/mut/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# run.sh : 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 diff --git a/agents/darkwing/work/s40-review/r3/mut/summary.txt b/agents/darkwing/work/s40-review/r3/mut/summary.txt new file mode 100644 index 00000000..e0315b9d --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/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 diff --git a/agents/darkwing/work/s40-review/r3/out/node-control-board.txt b/agents/darkwing/work/s40-review/r3/out/node-control-board.txt new file mode 100644 index 00000000..2133ae99 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/node-control-board.txt @@ -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 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 :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 (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 diff --git a/agents/darkwing/work/s40-review/r3/out/node-conversation.txt b/agents/darkwing/work/s40-review/r3/out/node-conversation.txt new file mode 100644 index 00000000..017970a5 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/node-conversation.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/node-webui.txt b/agents/darkwing/work/s40-review/r3/out/node-webui.txt new file mode 100644 index 00000000..dea58358 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/node-webui.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/probe-hold-edges-Mr.txt b/agents/darkwing/work/s40-review/r3/out/probe-hold-edges-Mr.txt new file mode 100644 index 00000000..136f1a92 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/probe-hold-edges-Mr.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/probe-hold-edges.txt b/agents/darkwing/work/s40-review/r3/out/probe-hold-edges.txt new file mode 100644 index 00000000..10ae7362 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/probe-hold-edges.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/probe-hold-order.txt b/agents/darkwing/work/s40-review/r3/out/probe-hold-order.txt new file mode 100644 index 00000000..d85b971c --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/probe-hold-order.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/probe-quit-base.txt b/agents/darkwing/work/s40-review/r3/out/probe-quit-base.txt new file mode 100644 index 00000000..48afadef --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/probe-quit-base.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/probe-stacked-later.txt b/agents/darkwing/work/s40-review/r3/out/probe-stacked-later.txt new file mode 100644 index 00000000..02709406 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/probe-stacked-later.txt @@ -0,0 +1,2 @@ +candidate: sent ["hi","yo"]; held null; quit true +Mr-no-recompute: sent []; held "hi\ryo\r\u0003"; quit false diff --git a/agents/darkwing/work/s40-review/r3/out/probe-survivors.txt b/agents/darkwing/work/s40-review/r3/out/probe-survivors.txt new file mode 100644 index 00000000..9d210b87 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/probe-survivors.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/summary.txt b/agents/darkwing/work/s40-review/r3/out/summary.txt new file mode 100644 index 00000000..75b3d7fe --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/summary.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-auth.txt b/agents/darkwing/work/s40-review/r3/out/test-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-auth.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-conductor.txt b/agents/darkwing/work/s40-review/r3/out/test-conductor.txt new file mode 100644 index 00000000..26554617 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-conductor.txt @@ -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 + +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 + +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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-config.txt b/agents/darkwing/work/s40-review/r3/out/test-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-config.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-discord.txt b/agents/darkwing/work/s40-review/r3/out/test-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-discord.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-extension-package.txt b/agents/darkwing/work/s40-review/r3/out/test-extension-package.txt new file mode 100644 index 00000000..ec3e7da0 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-extension-package.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-foundation.txt b/agents/darkwing/work/s40-review/r3/out/test-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-foundation.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-queue.txt b/agents/darkwing/work/s40-review/r3/out/test-queue.txt new file mode 100644 index 00000000..c1759574 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-queue.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-release-docker.txt b/agents/darkwing/work/s40-review/r3/out/test-release-docker.txt new file mode 100644 index 00000000..7202b85d --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-release-docker.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-release.txt b/agents/darkwing/work/s40-review/r3/out/test-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-release.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/out/test-task.txt b/agents/darkwing/work/s40-review/r3/out/test-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/out/test-task.txt @@ -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 diff --git a/agents/darkwing/work/s40-review/r3/probe/hold-edges.mjs b/agents/darkwing/work/s40-review/r3/probe/hold-edges.mjs new file mode 100644 index 00000000..2773a98f --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/probe/hold-edges.mjs @@ -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); diff --git a/agents/darkwing/work/s40-review/r3/probe/hold-order.mjs b/agents/darkwing/work/s40-review/r3/probe/hold-order.mjs new file mode 100644 index 00000000..99297fad --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/probe/hold-order.mjs @@ -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(" -> ")}`); +} diff --git a/agents/darkwing/work/s40-review/r3/probe/quit-base.mjs b/agents/darkwing/work/s40-review/r3/probe/quit-base.mjs new file mode 100644 index 00000000..735f51f8 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/probe/quit-base.mjs @@ -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); diff --git a/agents/darkwing/work/s40-review/r3/probe/stacked-later.mjs b/agents/darkwing/work/s40-review/r3/probe/stacked-later.mjs new file mode 100644 index 00000000..82733fbf --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/probe/stacked-later.mjs @@ -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); diff --git a/agents/darkwing/work/s40-review/r3/probe/survivors.mjs b/agents/darkwing/work/s40-review/r3/probe/survivors.mjs new file mode 100644 index 00000000..bc4efa26 --- /dev/null +++ b/agents/darkwing/work/s40-review/r3/probe/survivors.mjs @@ -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 }); diff --git a/agents/darkwing/work/s40-review/review-r3.md b/agents/darkwing/work/s40-review/review-r3.md new file mode 100644 index 00000000..44e6c6bf --- /dev/null +++ b/agents/darkwing/work/s40-review/review-r3.md @@ -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).