diff --git a/agents/darkwing/work/queue-50-review/r1/candidate-manifest.sha256 b/agents/darkwing/work/queue-50-review/r1/candidate-manifest.sha256 new file mode 100644 index 00000000..2b6a4a4f --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/candidate-manifest.sha256 @@ -0,0 +1,8 @@ +cfed0de2a8df99b15bc41509a4fb6a85fe70364fbae4c6e086aa9d771d18f678 agents/dewey/work/queue-50/evidence.md +6bb747ac074012014070a1ec69582055a7cb633c3c702bd047b54402ea28ed57 packages/conversation/README.md +17eb71ba3e84b6d59ed6e024fbf4055abab9df1d30368ce79dd0dc66f9477a4a packages/conversation/src/cohort.mjs +7d22038f2cded1ec58855734a5a762671b4faa9003c6e47fe85be316f93ee970 packages/conversation/src/controller.mjs +81b2d411f634601f923efb41c0e3dd2dcacccbf3224e30906e210b752de58a60 packages/conversation/tests/claim.test.mjs +669999461ad994060d1414f517f2e9648b672a2d5c12aa6b19e0bc5561cfb249 packages/conversation/tests/cohort.test.mjs +14f58a00844dab0cc25c993221a2651052df9fde57c9d94122b79093d8d521ac packages/conversation/tests/fake-pi.mjs +e92bdb29ec5c33d490ab308581f82bb7dada08c741d23edbd9bde6a29cc44a49 packages/conversation/tests/harness.mjs diff --git a/agents/darkwing/work/queue-50-review/r1/gate.sh b/agents/darkwing/work/queue-50-review/r1/gate.sh new file mode 100755 index 00000000..68642da7 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/gate.sh @@ -0,0 +1,19 @@ +#!/bin/bash +export TMPDIR=~/darkwing-scratch/r50a/tmp DOCKER_HOST=unix:///nonexistent.sock +cd ~/darkwing-scratch/r50a/wt +O=~/darkwing-scratch/r50a/out +: > $O/summary.txt +shims() { ps -eo pid,args | grep -F "$TMPDIR" | grep -F shim.mjs | grep -v grep | wc -l; } +units() { systemctl --user list-units --all 'mosaic-chat-*' --no-legend | wc -l; } +echo "start $(date -u +%FT%TZ) shims=$(shims) units=$(units)" >> $O/summary.txt +for p in conversation webui; do + node --test "packages/$p/tests/*.test.mjs" > $O/node-$p.txt 2>&1; e=$? + echo "node-$p exit=$e $(grep -E '^ℹ (pass|fail)' $O/node-$p.txt | tr '\n' ' ') shims-left=$(shims) units=$(units)" >> $O/summary.txt +done +for f in scripts/test-*.sh; do + s=$(basename $f .sh) + $f > $O/$s.txt 2>&1; e=$? + echo "$s exit=$e $(grep -E 'passed, [0-9]+ failed' $O/$s.txt | tail -1)" >> $O/summary.txt +done +echo "end $(date -u +%FT%TZ) shims-left=$(shims) units=$(units)" >> $O/summary.txt +echo DONE >> $O/summary.txt diff --git a/agents/darkwing/work/queue-50-review/r1/mut/closenoproof-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/closenoproof-conversation.txt new file mode 100644 index 00000000..bbc2a40a --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/closenoproof-conversation.txt @@ -0,0 +1,179 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (214.692766ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (35.281571ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.163359ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (364.532312ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (377.764228ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (300.852278ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (329.43766ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (281.021404ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.49468ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11462.013958ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (41221.895274ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (262.033375ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (144.24079ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (650.326566ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (291.322554ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (352.891003ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (109.201416ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (215.426382ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (287.170582ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (956.719345ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (237.403628ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (94.218119ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (124.467226ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (5.326433ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.301771ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (3.089689ms) +✔ no shim from this file's tests is left running for the after hook (27.435756ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (946.55456ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (551.363423ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (902.476944ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3019.596125ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (721.67872ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (789.127435ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (736.235267ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5543.534063ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (721.597132ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (816.575302ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (619.658275ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (672.240755ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (428.934148ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (447.676577ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3244.73205ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (28.056269ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (331.551762ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (289.443985ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.397498ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (660.379397ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (716.030096ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1571.840369ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (680.697711ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (469.881981ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (383.359469ms) +✔ no shim from this file's tests is left running for the after hook (25.614452ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (213.913878ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (202.510628ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (208.304608ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (277.193011ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (183.688541ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.502702ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (284.669635ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (425.864917ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (253.762093ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (223.546652ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (222.749566ms) +✔ 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 (219.827577ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (9.822021ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (205.914706ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (233.034193ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (260.984066ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (214.478911ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.384792ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.255966ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.314452ms) +✔ 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.471567ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.823854ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.392649ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.671493ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.346263ms) +✔ 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.324006ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.09485ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (221.320831ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (256.999033ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (287.066551ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (914.50853ms) +✔ H4: self-takeover is refused (342.427544ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (502.664919ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1079.151522ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (246.762256ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (572.624718ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (266.594445ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (209.636318ms) +✔ H13: a retry with the same request ID and different text is refused (190.364234ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (612.176306ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (300.766877ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (219.145789ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (908.054339ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (330.972574ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (759.177034ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (203.329728ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (344.558923ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.704325ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (655.713075ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (559.332315ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (683.460323ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2988.767776ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (813.104398ms) +✔ no shim from this file's tests is left running for the after hook (25.100745ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.76514ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.764145ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.860771ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.501483ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.559726ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.99225ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.883923ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.251287ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.715171ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.551748ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.590313ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.530809ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.051209ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.22648ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.599878ms) +✔ F9: registrations never add or redirect a root (1.922949ms) +✔ F10: a header cwd naming another project is refused (4.623169ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.738107ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.765454ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.879358ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.364377ms) +✔ F13: compaction is a marker in place, then the retained content (0.712237ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (550.683981ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.207212ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.826563ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.617974ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.249975ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.523566ms) +✔ every page and cursor is a valid CHAT-01 record (847.570613ms) +✔ the engine pin holds for the installed package (1.814212ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (274.302459ms) +✔ 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.342398ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (284.79393ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (485.972173ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (430.186236ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (224.474896ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.01273ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (575.68202ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (268.344177ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (318.54659ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (496.492255ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1910.454833ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (193.28303ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (300.805643ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (200.751771ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (225.31464ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (428.690849ms) +✔ N10: fake conformance (31.727507ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (465.047877ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (275.008046ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (298.293847ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (369.68525ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (341.912018ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (363.219305ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (210.340268ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (337.62287ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (597.654958ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (477.886696ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (476.354205ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (202.361269ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (203.626415ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (185.493095ms) +✔ 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 (266.574958ms) +✔ 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 (191.942472ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 171 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 61552.029333 diff --git a/agents/darkwing/work/queue-50-review/r1/mut/closenoproof-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/closenoproof-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/closenoproof-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/early-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/early-conversation.txt new file mode 100644 index 00000000..daeb5410 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/early-conversation.txt @@ -0,0 +1,197 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (226.538765ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (34.268642ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.422735ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (377.164568ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (431.451613ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (280.533261ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (177.598291ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (184.000175ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.606349ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11780.12298ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (34457.495069ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (250.688997ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (147.953925ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (619.220498ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (271.517832ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (337.776412ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (99.431855ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (191.77796ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (266.716649ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (754.992557ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (195.164155ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (88.420933ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (73.334283ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.091711ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.06084ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.714365ms) +✔ no shim from this file's tests is left running for the after hook (24.687783ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (966.015522ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (459.470974ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (785.668158ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2972.466637ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (689.839064ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (783.388241ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (694.662392ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5805.491938ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (950.505915ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (889.439303ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (654.781853ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (651.721686ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (305.187926ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (458.226114ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3222.831765ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (22.901421ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (380.091672ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (306.358925ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (245.937337ms) +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (8752.531755ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (484.261434ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1321.458437ms) +✖ R4: after a normal engine exit the scope stays until a proven force stop releases it (8638.805908ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (468.847575ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (135.291846ms) +✔ no shim from this file's tests is left running for the after hook (24.858227ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (230.941401ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (200.205285ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (203.404644ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (285.956646ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (199.274117ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.658149ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (198.814934ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (297.38075ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (199.967743ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (184.942994ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (205.001694ms) +✔ 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 (207.14911ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.166387ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (191.050687ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (212.584924ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (238.556153ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (188.14997ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.428012ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.27284ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.310939ms) +✔ 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.505556ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.312106ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.492183ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.771178ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.401813ms) +✔ 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.349021ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.102857ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (217.163099ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (252.50476ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (292.737298ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (799.408326ms) +✔ H4: self-takeover is refused (180.422123ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (441.138012ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (961.852511ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (164.639843ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (493.79832ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (241.455843ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (126.772101ms) +✔ H13: a retry with the same request ID and different text is refused (175.726861ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (537.948256ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (270.40096ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (192.225324ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (819.091249ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (320.018994ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (738.858601ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (186.015594ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (435.090407ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.710491ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (788.678038ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (573.59876ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (729.083187ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2922.735072ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (628.576787ms) +✔ no shim from this file's tests is left running for the after hook (27.686027ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (10.335546ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.974704ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.982817ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (4.177387ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.695545ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.242896ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.947402ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.345194ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.122195ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.859061ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.658951ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (6.133022ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (9.720826ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (18.984176ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.866166ms) +✔ F9: registrations never add or redirect a root (1.818595ms) +✔ F10: a header cwd naming another project is refused (4.150549ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.90349ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.036037ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.594452ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.937937ms) +✔ F13: compaction is a marker in place, then the retained content (0.924937ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (574.383504ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.399028ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.421105ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.162867ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.025159ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.827236ms) +✔ every page and cursor is a valid CHAT-01 record (910.588679ms) +✔ the engine pin holds for the installed package (1.996312ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (316.214368ms) +✔ 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) (590.898879ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (298.624996ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (514.208689ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (446.016438ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (237.853584ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.913513ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (346.327299ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (193.339483ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (287.50742ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (451.71746ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1856.58794ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (178.411715ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (268.200087ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (135.709863ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (219.988297ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (328.423857ms) +✔ N10: fake conformance (32.65928ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (412.128686ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (260.165173ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (277.358165ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (356.672295ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (348.833065ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (338.555879ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (296.757486ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (537.564445ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (720.638485ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (474.091393ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (481.958163ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (241.457842ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (147.404828ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (191.910818ms) +✔ 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 (217.258047ms) +✔ 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 (177.846399ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 169 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 54448.602276 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:767:1 +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (8752.531755ms) + Error: timed out waiting for the release + at until (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:774:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) + +test at packages/conversation/tests/cohort.test.mjs:855:1 +✖ R4: after a normal engine exit the scope stays until a proven force stop releases it (8638.805908ms) + Error: timed out waiting for the release + at until (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:869:5) + 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/queue-50-review/r1/mut/early-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/early-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/early-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/invnull-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/invnull-conversation.txt new file mode 100644 index 00000000..3de29f84 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/invnull-conversation.txt @@ -0,0 +1,179 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (217.437364ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (35.287716ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.275383ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (435.025429ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (469.934947ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (235.169757ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (188.361554ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (194.945317ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.686824ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12004.075886ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39780.915576ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (262.389653ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (145.367001ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (692.277541ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (277.712728ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (333.266579ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (104.5588ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (188.451758ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (248.869272ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (715.242301ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (175.024098ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (85.693007ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (74.073144ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.1928ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.016159ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.744248ms) +✔ no shim from this file's tests is left running for the after hook (24.811191ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (681.678313ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (586.381147ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (576.815328ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2994.094089ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (706.883271ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (777.080197ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (720.20232ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5741.110911ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1022.386797ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (877.425463ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (608.740496ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (729.442732ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (339.019259ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (532.059847ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3300.221832ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (32.31475ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (343.075206ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (658.700292ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.772229ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (767.246826ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (740.516167ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1109.722632ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (654.971565ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (446.856951ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (115.816679ms) +✔ no shim from this file's tests is left running for the after hook (23.85077ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (213.313264ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (261.701969ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (199.898389ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (418.474269ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (190.693165ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.435084ms) +✔ 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.457992ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (291.753187ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (215.302269ms) +✔ 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.438552ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (213.869494ms) +✔ 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 (201.302397ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.237925ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (214.146727ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (270.063789ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (311.130976ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (223.387591ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.406729ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.263697ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.324578ms) +✔ 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.483082ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.055237ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.498732ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.798485ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.467227ms) +✔ 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.356714ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.10348ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (234.07889ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (259.557245ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (352.16091ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (907.683249ms) +✔ H4: self-takeover is refused (187.73179ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (440.424074ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1164.253826ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (241.239331ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (573.404858ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (256.51688ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (193.027945ms) +✔ H13: a retry with the same request ID and different text is refused (197.708066ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (591.28546ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (282.4104ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (194.783597ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (886.689645ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (311.897148ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (751.010704ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (208.512644ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (358.334627ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.70851ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (671.255297ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (556.285268ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (787.434113ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2957.376126ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (661.778715ms) +✔ no shim from this file's tests is left running for the after hook (26.236506ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.95878ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.567562ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.651404ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.56735ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.513114ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.48256ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.910714ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.285721ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.69642ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.261767ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (6.180982ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.006691ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.177121ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.805197ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.411125ms) +✔ F9: registrations never add or redirect a root (2.26304ms) +✔ F10: a header cwd naming another project is refused (4.895154ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.884505ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.486557ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.441582ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.561291ms) +✔ F13: compaction is a marker in place, then the retained content (1.034111ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (509.355266ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.083237ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.580284ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.473366ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.275457ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.101512ms) +✔ every page and cursor is a valid CHAT-01 record (747.525123ms) +✔ the engine pin holds for the installed package (1.784386ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (264.135133ms) +✔ 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) (531.936436ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (237.293073ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (551.245113ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (576.311424ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (225.332594ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.065037ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (338.37759ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (197.412407ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (295.772071ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (472.33043ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1912.031085ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (198.570749ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (288.331066ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (186.557763ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (207.68415ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (355.674046ms) +✔ N10: fake conformance (31.915637ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (450.594266ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (283.377092ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (290.965603ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (356.663196ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (361.616015ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (370.687476ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (208.330566ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (338.791689ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (641.803164ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (486.758085ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (553.977848ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (249.946582ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (195.157166ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (206.967544ms) +✔ 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 (235.94119ms) +✔ 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 (207.906207ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 171 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60060.417812 diff --git a/agents/darkwing/work/queue-50-review/r1/mut/invnull-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/invnull-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/invnull-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/manifest-after.txt b/agents/darkwing/work/queue-50-review/r1/mut/manifest-after.txt new file mode 100644 index 00000000..d0465a08 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/manifest-after.txt @@ -0,0 +1,7 @@ +packages/conversation/README.md: OK +packages/conversation/src/cohort.mjs: OK +packages/conversation/src/controller.mjs: OK +packages/conversation/tests/claim.test.mjs: OK +packages/conversation/tests/cohort.test.mjs: OK +packages/conversation/tests/fake-pi.mjs: OK +packages/conversation/tests/harness.mjs: OK diff --git a/agents/darkwing/work/queue-50-review/r1/mut/mutate.py b/agents/darkwing/work/queue-50-review/r1/mut/mutate.py new file mode 100644 index 00000000..58750307 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/mutate.py @@ -0,0 +1,41 @@ +# mutate.py : apply one named mutant (each edit exact text, must match once). Run from the tree root. +import sys +C = "packages/conversation/" +CT = C + "src/controller.mjs" +CO = C + "src/cohort.mjs" +RELSTOP = (CT, " await this.#releaseScope(\"force-stop\", proven);\n", "") +RELCLOSE = (CT, " if (this.b?.state === \"stopped\" && this.#stopped(this.stops.get(this.b.stop))) await this.#releaseScope(\"close\");\n", "") +GUARD = " if (!rec || rec.state !== \"stopped\" || rec.proof?.kind !== \"cohortProof\" || !rec.shim) return null;\n" +M = { + "norelstop": [RELSTOP], + "norelclose": [RELCLOSE], + "norel": [RELSTOP, RELCLOSE], + # releaseCohort reports the unit absent without looking. + "nowait": [(CO, " const end = Date.now() + waitMs;\n", " return { outcome: \"released\", unit: \"absent\" };\n const end = Date.now() + waitMs;\n")], + # releaseCohort doesn't wait for systemd at all, and says so. + "wait0": [(CO, "shimSocket, waitMs = 3000 })", "shimSocket, waitMs = 0 })")], + # #releaseScope keeps only the shim check; callers unchanged. + "noguard": [(CT, GUARD, " if (!rec || !rec.shim) return null;\n")], + # close releases whenever the binding reads stopped, with no #stopped check. + "closenoproof": [(CT, " if (this.b?.state === \"stopped\" && this.#stopped(this.stops.get(this.b.stop))) await", " if (this.b?.state === \"stopped\") await")], + # The release result isn't pushed to observers. + "nopush": [(CT, " this.#push({ kind: \"evidence\", evidence: entry });\n", "")], + # The release result isn't recorded in evidence.releases. + "noevid": [(CT, " this.evidence.releases.push(entry);\n", "")], + # releaseCohort drops the null-ID check. + "invnull": [(CO, "if (!invocationId || hello.invocationId !== invocationId)", "if (hello.invocationId !== invocationId)")], + # releaseCohort sends release with no hello and no ID check. + "nohello": [(CO, " const hello = await shimRequest(shimSocket, \"hello\");\n if (!hello.ok) return { outcome: \"unavailable\", reason: hello.unavailable };\n if (!invocationId || hello.invocationId !== invocationId) return { outcome: \"unavailable\", reason: \"the shim does not answer for the recorded scope; nothing released\" };\n", "")], + # The force stop releases before the claim records stopped (guard intact). + "early": [RELSTOP, (CT, " let session = null;\n try {\n session = this.#readSession();", " await this.#releaseScope(\"force-stop\", this.claim);\n let session = null;\n try {\n session = this.#readSession();")], + # N2: killShims signals any unit. + "n2": [(C + "tests/harness.mjs", " if (!/^mosaic-chat-/.test(s.unit ?? \"\")) {\n", " if (false) {\n")], +} +if "--files" in sys.argv: + print(" ".join(sorted({f for f, _, _ in M[sys.argv[1]]}))); sys.exit(0) +for f, old, new in M[sys.argv[1]]: + s = open(f).read() + n = s.count(old) + if n != 1: sys.exit(f"{sys.argv[1]}: {n} matches in {f}") + open(f, "w").write(s.replace(old, new)) + print(f) diff --git a/agents/darkwing/work/queue-50-review/r1/mut/n2-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/n2-conversation.txt new file mode 100644 index 00000000..611498ff --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/n2-conversation.txt @@ -0,0 +1,206 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (214.832977ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (34.571677ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.879732ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (349.856205ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (468.272065ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (246.271703ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (192.56229ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (182.938147ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.954834ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12454.017135ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39976.388886ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (262.294369ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (142.541432ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (624.846842ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (286.900564ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (342.941382ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (104.478382ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (190.144174ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (268.593624ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (753.254962ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (186.586663ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (93.32013ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (64.398658ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.057996ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.037509ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.746168ms) +✔ no shim from this file's tests is left running for the after hook (23.881335ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1036.304245ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (452.443886ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (735.320365ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2967.485553ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (715.430564ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (825.969742ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (924.961689ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5737.072887ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (942.242091ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (905.786674ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (693.53554ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (775.787553ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (414.400446ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (442.580709ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3274.837948ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (24.64145ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (507.74544ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (286.272ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (100.961673ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (784.259864ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (768.208234ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1354.770418ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (675.069233ms) +✖ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (453.184638ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (134.193267ms) +✔ no shim from this file's tests is left running for the after hook (23.312722ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (207.223917ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (206.080832ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (191.759441ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (339.985059ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (197.965187ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.47993ms) +✔ 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.687283ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (296.861259ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (209.740754ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (178.594626ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (195.227527ms) +✔ 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.925959ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.060451ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (203.422354ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (207.897951ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (230.896474ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (217.397873ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.404488ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.278864ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.292223ms) +✔ 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.521121ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.01716ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.49446ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.739719ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.366934ms) +✔ 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.351546ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.103886ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (224.942482ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (253.890125ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (293.872273ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (868.419123ms) +✔ H4: self-takeover is refused (179.079168ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (418.289419ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (990.164509ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (230.250826ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (537.189837ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (264.986708ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (124.086351ms) +✔ H13: a retry with the same request ID and different text is refused (171.589576ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (598.687091ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (286.895845ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (199.560486ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (869.005692ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (438.820807ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1007.925195ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (221.259857ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (345.731774ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.824252ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (682.841869ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (602.766646ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (674.939883ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3399.227382ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (680.860103ms) +✔ no shim from this file's tests is left running for the after hook (30.517338ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.794644ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.662698ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.64933ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.495017ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.494239ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.989199ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.902555ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.329506ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.568865ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.766891ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.988037ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.686158ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.598091ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.271214ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.931874ms) +✔ F9: registrations never add or redirect a root (1.698626ms) +✔ F10: a header cwd naming another project is refused (4.397501ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.79825ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.444411ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.201517ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.572177ms) +✔ F13: compaction is a marker in place, then the retained content (0.796574ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (501.901364ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.31991ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.386812ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.091305ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.087751ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.778953ms) +✔ every page and cursor is a valid CHAT-01 record (772.978757ms) +✔ the engine pin holds for the installed package (2.858036ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (261.191961ms) +✔ 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) (513.396416ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (258.172227ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (502.50312ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (485.301356ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (237.887749ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.972247ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (347.278229ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (187.964679ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (298.042589ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (472.695037ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1869.203815ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (198.034335ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (283.172908ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (194.256775ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (202.536671ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (396.115669ms) +✔ N10: fake conformance (30.808263ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (444.599863ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (270.756574ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (374.4837ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (575.793725ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (389.685398ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (380.19316ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (142.851081ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (317.626646ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (742.694744ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (487.790495ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (483.990364ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (230.539183ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (190.167064ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (401.425992ms) +✔ 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.176688ms) +✔ 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 (179.416997ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 170 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60546.123145 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:877:1 +✖ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (453.184638ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [] + - [ + - [ + - 438467, + - 'r5-not-mosaic-cfc8601f3b56' + - ] + - ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:898:14) + 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: [], + expected: [ [ 438467, 'r5-not-mosaic-cfc8601f3b56' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-50-review/r1/mut/n2-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/n2-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/n2-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/noevid-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/noevid-conversation.txt new file mode 100644 index 00000000..95c395d1 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/noevid-conversation.txt @@ -0,0 +1,224 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (207.636312ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (31.671819ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.531549ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (365.224435ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (375.743275ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (238.900008ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (225.805156ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (299.615632ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.531001ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (13253.125745ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39168.220723ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (265.09423ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (139.935762ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (610.02107ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (282.459774ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (329.709422ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (215.645266ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (283.574969ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (499.324428ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (949.098027ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (199.013326ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (91.935987ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (85.208259ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.098139ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.436034ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.844956ms) +✔ no shim from this file's tests is left running for the after hook (24.673716ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (976.65867ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (475.498215ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (942.748798ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3043.619425ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (1136.345293ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (888.096775ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (768.265805ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5881.341427ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1038.181376ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (778.528574ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (454.270423ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (731.569118ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (487.686365ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (880.595621ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3957.867674ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (60.917654ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (595.755511ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (476.456199ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.135937ms) +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (8643.176352ms) +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (576.569113ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1317.296796ms) +✖ R4: after a normal engine exit the scope stays until a proven force stop releases it (8688.575873ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (596.211364ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (122.08846ms) +✔ no shim from this file's tests is left running for the after hook (25.976516ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (202.474345ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (216.296105ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (198.638065ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (278.82666ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (194.620148ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.437381ms) +✔ 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.939948ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (341.197899ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (337.851502ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (295.460905ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (239.146079ms) +✔ 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.685473ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.981311ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (214.520823ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (221.356733ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (274.526795ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (217.87438ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.583858ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.288987ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.331664ms) +✔ 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.50306ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.957714ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.461908ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.689579ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.378544ms) +✔ 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.335821ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.093501ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (225.033292ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (246.890313ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (312.851641ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (811.813782ms) +✔ H4: self-takeover is refused (297.074042ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (617.007528ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1112.052305ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (251.177186ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (552.166906ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (270.296071ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (174.004826ms) +✔ H13: a retry with the same request ID and different text is refused (239.627211ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (893.349674ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (654.644864ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (223.473935ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (972.900194ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (384.526377ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (830.365315ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (235.744185ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (364.981386ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.648115ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (726.725677ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (609.885128ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (777.19598ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3161.318197ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (772.608145ms) +✔ no shim from this file's tests is left running for the after hook (26.243188ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.924855ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.71635ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.910233ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.719744ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.375009ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.976274ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.811346ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.381024ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.412276ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.38275ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.192834ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.362975ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.411819ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.614048ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.774105ms) +✔ F9: registrations never add or redirect a root (1.893373ms) +✔ F10: a header cwd naming another project is refused (5.077911ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.860447ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.159008ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.934562ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.296195ms) +✔ F13: compaction is a marker in place, then the retained content (0.713412ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (514.555301ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.961625ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.499461ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.509135ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.110792ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.049583ms) +✔ every page and cursor is a valid CHAT-01 record (771.592435ms) +✔ the engine pin holds for the installed package (1.839777ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (258.023466ms) +✔ 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) (534.072833ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (237.137804ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (499.752552ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (433.973546ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (227.498429ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.085678ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (408.771003ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (319.475731ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (422.64905ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (536.684562ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1926.54673ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (261.85206ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (526.165066ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (292.165984ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (556.174851ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (391.852322ms) +✔ N10: fake conformance (31.082605ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (478.572614ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (311.539451ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (327.69438ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (377.85985ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (406.809075ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (415.148898ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (223.174587ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (362.138514ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (823.257562ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (522.049384ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (609.470456ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (310.027929ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (311.932454ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (200.59052ms) +✔ 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 (241.550105ms) +✔ 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 (205.758549ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 168 +ℹ fail 3 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 62347.19459 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:767:1 +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (8643.176352ms) + Error: timed out waiting for the release + at until (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:774:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) + +test at packages/conversation/tests/cohort.test.mjs:784:1 +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (576.569113ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [] + - [ + - [ + - 'close', + - 'released', + - 'mosaic-chat-c113b34935ebb759fe6dcdcb4', + - 'absent' + - ] + - ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:797: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: [], + expected: [ [ 'close', 'released', 'mosaic-chat-c113b34935ebb759fe6dcdcb4', 'absent' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/conversation/tests/cohort.test.mjs:855:1 +✖ R4: after a normal engine exit the scope stays until a proven force stop releases it (8688.575873ms) + Error: timed out waiting for the release + at until (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:869:5) + 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/queue-50-review/r1/mut/noevid-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/noevid-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/noevid-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/noguard-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/noguard-conversation.txt new file mode 100644 index 00000000..3b16dfa8 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/noguard-conversation.txt @@ -0,0 +1,179 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (242.454215ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (52.417607ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (160.65193ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (691.684457ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (432.820347ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (253.91496ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (211.614144ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (208.267027ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.537314ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11470.871921ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38939.018387ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (278.675248ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (154.656012ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (646.680456ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (278.585947ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (359.19416ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (105.386296ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (202.941484ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (259.142319ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (827.508601ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (186.58325ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (96.006657ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (69.708438ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.133499ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.090929ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.773917ms) +✔ no shim from this file's tests is left running for the after hook (23.991006ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1415.983918ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (524.641874ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (605.031463ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2831.337028ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (576.370739ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (640.256395ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (506.917941ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5379.297787ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (778.851305ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (641.936043ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (520.03173ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (748.983034ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (330.971784ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (534.744906ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3289.235238ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (38.967813ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (343.168178ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (343.128554ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (182.540451ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (602.938699ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (606.854991ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1314.766263ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (930.984706ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (480.884572ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (142.339444ms) +✔ no shim from this file's tests is left running for the after hook (25.722944ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (247.351219ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (420.645996ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (521.474171ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (341.666185ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (240.156116ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.40418ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (231.245874ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (319.232063ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (221.250466ms) +✔ 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.770254ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (211.581179ms) +✔ 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 (215.479606ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.898457ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (225.85683ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (238.501084ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (275.812845ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (197.9187ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.423836ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.270355ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.321871ms) +✔ 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.476881ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.293449ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.496557ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.904074ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.480359ms) +✔ 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.348958ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.104714ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (240.114931ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (449.349522ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (659.741702ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (935.32517ms) +✔ H4: self-takeover is refused (208.479759ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (447.288998ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1098.784903ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (205.142031ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (648.251043ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (462.685827ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (328.233583ms) +✔ H13: a retry with the same request ID and different text is refused (265.184216ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (616.056402ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (308.527542ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (192.810519ms) +✔ 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.60304ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (339.630756ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (712.634315ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (189.604469ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (318.34834ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.636126ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (665.523681ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (547.749173ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (705.869893ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2891.07586ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (642.516066ms) +✔ no shim from this file's tests is left running for the after hook (24.823327ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (9.549304ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.772234ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.76215ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.905107ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.598993ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.057413ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.904387ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.248313ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.214597ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.929992ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.295887ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.284949ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.156042ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.505119ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.595964ms) +✔ F9: registrations never add or redirect a root (1.63173ms) +✔ F10: a header cwd naming another project is refused (3.701919ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.657098ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.816836ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.963921ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.411975ms) +✔ F13: compaction is a marker in place, then the retained content (0.82287ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (538.008331ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.843446ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.46375ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.297425ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.114937ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.057435ms) +✔ every page and cursor is a valid CHAT-01 record (774.21722ms) +✔ the engine pin holds for the installed package (2.880747ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (274.936394ms) +✔ 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) (541.096937ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (260.530316ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (1004.674992ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (554.175472ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (290.60681ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.238008ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (390.977012ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (205.52208ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (316.814445ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (502.975006ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2028.624891ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (318.597754ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (330.129325ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (202.937284ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (229.999463ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (415.225114ms) +✔ N10: fake conformance (31.200214ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (422.298086ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (261.198128ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (278.256096ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (346.926126ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (352.988913ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (323.874357ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (191.324181ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (323.262106ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (664.737663ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (507.685055ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (537.623524ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (241.815985ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (199.432894ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (160.372641ms) +✔ 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 (218.528118ms) +✔ 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 (191.594051ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 171 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 59472.098959 diff --git a/agents/darkwing/work/queue-50-review/r1/mut/noguard-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/noguard-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/noguard-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/nohello-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/nohello-conversation.txt new file mode 100644 index 00000000..e9d46094 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/nohello-conversation.txt @@ -0,0 +1,200 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (215.175505ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.123911ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.947087ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (361.835331ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (373.005155ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (238.513988ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (205.167951ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (202.383689ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.419459ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11684.686206ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39230.305323ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (236.41881ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (147.287367ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (613.636414ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (275.578534ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (341.422176ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (107.676756ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (198.072221ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (266.304495ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (730.669406ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (187.110567ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (95.851048ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (76.884194ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.539764ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.075101ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.806082ms) +✔ no shim from this file's tests is left running for the after hook (25.068002ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (997.736595ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (469.162088ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (804.119799ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2997.757121ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (729.52872ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (792.205153ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (673.905973ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5486.250965ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (990.216545ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (837.495754ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (642.639592ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (678.955973ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (350.077895ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (518.641333ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3228.488102ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (23.172026ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (302.450932ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (260.286668ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (80.932038ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (653.46987ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (656.712268ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1060.158703ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (628.991464ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (446.077873ms) +✖ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (331.786453ms) +✔ no shim from this file's tests is left running for the after hook (25.018511ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (188.241301ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (199.683994ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (193.024131ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (277.606543ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (194.693048ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.415239ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (198.310138ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (306.778869ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (206.138101ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (186.176669ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (211.784032ms) +✔ 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 (225.731411ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.913672ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (236.515098ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (225.947569ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (243.6901ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (196.128802ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.405605ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.279138ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.326428ms) +✔ 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.494175ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.983743ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.45172ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.702253ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.366719ms) +✔ 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.331497ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.101143ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (223.054032ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (250.788056ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (287.389348ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (814.418228ms) +✔ H4: self-takeover is refused (195.676861ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (449.066449ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1073.06667ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (143.437118ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (525.344102ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (285.372611ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (190.507347ms) +✔ H13: a retry with the same request ID and different text is refused (167.462773ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (700.21254ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (318.071983ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (196.686314ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (867.634832ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (326.053962ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (762.791653ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (203.681651ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (349.783785ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.620231ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (667.892791ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (595.329767ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (742.930505ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2985.741805ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (840.382607ms) +✔ no shim from this file's tests is left running for the after hook (25.361529ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.673021ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.518823ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.541892ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.29352ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.393107ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.794143ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.728582ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.271015ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.16817ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.241423ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.554433ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.648447ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.948451ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.568872ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.061596ms) +✔ F9: registrations never add or redirect a root (1.853088ms) +✔ F10: a header cwd naming another project is refused (3.964189ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.74024ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.214047ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.056795ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.35793ms) +✔ F13: compaction is a marker in place, then the retained content (0.810101ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (522.05669ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.37069ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.399285ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.119375ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.001825ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.84706ms) +✔ every page and cursor is a valid CHAT-01 record (753.612106ms) +✔ the engine pin holds for the installed package (1.79135ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (276.769999ms) +✔ 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.937309ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (242.117439ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (474.25551ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (430.225951ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (229.095402ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.780278ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (363.88483ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (201.996198ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (291.201265ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (538.966176ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1893.839ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (178.846046ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (339.902106ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (261.426397ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (215.160915ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (385.564727ms) +✔ N10: fake conformance (32.658519ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (434.018885ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (271.775909ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (288.577906ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (354.33718ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (365.912888ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (361.736564ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (210.771803ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (296.049537ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (692.843889ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (541.475301ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (529.983629ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (224.787674ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (215.336187ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (208.24288ms) +✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (223.435019ms) +✔ 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 (209.137688ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 170 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 59094.134786 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:912:1 +✖ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (331.786453ms) + AssertionError [ERR_ASSERTION]: scope another-invocation: {"outcome":"released","unit":"absent"} + + actual - expected + + + 'released' + - 'unavailable' + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:923:14) + 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: 'released', + expected: 'unavailable', + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-50-review/r1/mut/nohello-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/nohello-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/nohello-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/nopush-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/nopush-conversation.txt new file mode 100644 index 00000000..dbbf357e --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/nopush-conversation.txt @@ -0,0 +1,179 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (610.094505ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.12307ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (23.973168ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (384.038016ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (428.347034ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (241.353156ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (200.742859ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (206.818614ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.525433ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12124.530569ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (40839.021146ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (250.397046ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (139.425969ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (583.534778ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (283.083002ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (330.086525ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (105.6266ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (191.542767ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (259.1853ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (724.437134ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (181.303625ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (90.716408ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (77.665155ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.509003ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.062282ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.738483ms) +✔ no shim from this file's tests is left running for the after hook (27.373037ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1271.492539ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (458.680229ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (792.75075ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3054.894774ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (692.192928ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (786.21189ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (760.868817ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5717.843605ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (956.549684ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (851.987246ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (676.552246ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (636.004254ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (325.995728ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (605.183971ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3226.919695ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (31.317048ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (426.608535ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (351.518947ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (301.803126ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (754.50599ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (793.335128ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1460.706899ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (707.820782ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (657.499706ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (136.790576ms) +✔ no shim from this file's tests is left running for the after hook (23.117974ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (618.019409ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (224.991426ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (213.214083ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (322.033932ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (203.882941ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.436886ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (203.240369ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (299.252018ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (219.22757ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (186.117243ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (221.991726ms) +✔ 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 (210.761669ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.039911ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (211.16259ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (221.603278ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (242.256732ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (275.145616ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.588289ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.309744ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.382715ms) +✔ 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.60344ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.480713ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.558933ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.874015ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.480526ms) +✔ 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.39134ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.108651ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (242.468032ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (673.637213ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (306.495236ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (847.416441ms) +✔ H4: self-takeover is refused (195.993008ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (443.154085ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1069.92806ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (296.095444ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (710.574632ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (313.754055ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (226.490518ms) +✔ H13: a retry with the same request ID and different text is refused (202.547513ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (608.495252ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (291.863531ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (172.161863ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (869.340732ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (372.192425ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (729.785777ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (190.018824ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (334.35663ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.667452ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (663.382785ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (555.842185ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (669.501961ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3084.088943ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (662.615128ms) +✔ no shim from this file's tests is left running for the after hook (26.363449ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.61387ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.520597ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.553279ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.278146ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.547081ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.995354ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.832284ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.314726ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.314305ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (9.678218ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.811949ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.48356ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.049584ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.433738ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.874251ms) +✔ F9: registrations never add or redirect a root (1.763297ms) +✔ F10: a header cwd naming another project is refused (3.883323ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.65573ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.843136ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.94814ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.275445ms) +✔ F13: compaction is a marker in place, then the retained content (0.720388ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (666.350283ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.268648ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.513312ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.481289ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.172609ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.391348ms) +✔ every page and cursor is a valid CHAT-01 record (816.996812ms) +✔ the engine pin holds for the installed package (1.614015ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (249.664164ms) +✔ 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) (584.126993ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (274.485645ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (923.194597ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (486.09061ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (237.842834ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.339844ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (353.311948ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (210.430558ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (311.426841ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (487.947066ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1935.291269ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (203.686582ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (297.116229ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (204.010468ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (236.410034ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (353.208355ms) +✔ N10: fake conformance (31.847987ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (430.153169ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (289.77623ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (288.056445ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (365.472373ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (349.061445ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (341.141324ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (190.364759ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (340.080589ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (581.159525ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (520.387ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (382.727521ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (203.365545ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (160.159886ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (319.398856ms) +✔ 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 (140.229846ms) +✔ 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 (208.737698ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 171 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 61444.862992 diff --git a/agents/darkwing/work/queue-50-review/r1/mut/nopush-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/nopush-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/nopush-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/norel-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/norel-conversation.txt new file mode 100644 index 00000000..2d20661b --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/norel-conversation.txt @@ -0,0 +1,212 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (878.142482ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (38.642847ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (27.049701ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (423.115214ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (569.429638ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (291.024999ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (253.947539ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (369.718253ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.543172ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (13306.114572ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (42073.698569ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (417.874884ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (227.856985ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (685.775484ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (460.462733ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (583.079714ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (311.209318ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (370.989809ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (1009.400293ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (2258.036637ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (441.60858ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (165.913368ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (108.516068ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.474591ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.082517ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.800422ms) +✔ no shim from this file's tests is left running for the after hook (26.795539ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3270.19909ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (544.687107ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2669.762301ms) +✔ K4: two engines; force stop one; the other survives by independent observation (5019.494141ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2606.491841ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2636.874347ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2748.981285ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5266.075278ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (754.554968ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (633.525859ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (525.698051ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (1174.519814ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (615.154411ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (731.49351ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3416.71319ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (46.311676ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (562.402939ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (893.973144ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (86.294421ms) +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (10630.94396ms) +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (1481.599963ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1885.303906ms) +✖ R4: after a normal engine exit the scope stays until a proven force stop releases it (10743.860597ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (471.104443ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (136.404351ms) +✔ no shim from this file's tests is left running for the after hook (24.052178ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (253.606154ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (873.176732ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (244.669154ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (452.468999ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (235.925916ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.534407ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (240.495052ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (357.955535ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (389.798238ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (327.826574ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (253.655188ms) +✔ 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 (250.015847ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.004271ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (254.498193ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (324.03991ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (372.942129ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (391.276216ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.400551ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.271842ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.309679ms) +✔ 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.519175ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.955159ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.455663ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.642582ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.350907ms) +✔ 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.336209ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.098638ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (229.244155ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (931.86434ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (347.41319ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1085.910085ms) +✔ H4: self-takeover is refused (257.530138ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (744.914659ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1430.014975ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (442.990461ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1167.352159ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (439.2164ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (258.76511ms) +✔ H13: a retry with the same request ID and different text is refused (299.493422ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (772.790955ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (350.769507ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (379.676755ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1281.275523ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (436.494525ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (971.17841ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (247.735368ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (414.280176ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.629383ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (715.260501ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (707.128591ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (951.012134ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2918.165215ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (678.092719ms) +✔ no shim from this file's tests is left running for the after hook (25.84205ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.201719ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.090269ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.029281ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.52071ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.537232ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.247922ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.748357ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.263129ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.133351ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.893212ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.248625ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.739148ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.952127ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.336624ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.887238ms) +✔ F9: registrations never add or redirect a root (1.948292ms) +✔ F10: a header cwd naming another project is refused (4.378338ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.861924ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.579126ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.119264ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.57584ms) +✔ F13: compaction is a marker in place, then the retained content (0.840877ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (595.010664ms) +✔ fragments never cut a surrogate pair and keep an empty string (6.164318ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (2.538841ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.601523ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.160496ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.183738ms) +✔ every page and cursor is a valid CHAT-01 record (1110.90821ms) +✔ the engine pin holds for the installed package (2.212279ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (304.260419ms) +✔ 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) (547.754943ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (282.891716ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (1227.543291ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (642.528708ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (271.077926ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.35836ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (455.332863ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (369.101589ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (437.443115ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (584.582297ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2020.161461ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (269.943094ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (409.352777ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (267.963605ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (305.256749ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (557.010403ms) +✔ N10: fake conformance (32.716305ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (560.964562ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (320.358221ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (475.897401ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (611.31702ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (490.381303ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (436.854463ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (268.116114ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (446.490817ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (933.563392ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (673.656544ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (646.760684ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (418.093753ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (225.56854ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (211.51223ms) +✔ 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 (244.075296ms) +✔ 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 (240.028765ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 168 +ℹ fail 3 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 69590.726236 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:767:1 +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (10630.94396ms) + Error: timed out waiting for the release + at until (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:774:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) + +test at packages/conversation/tests/cohort.test.mjs:784:1 +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (1481.599963ms) + AssertionError [ERR_ASSERTION]: {"loadState":"loaded","activeState":"active","invocationId":"94b85a3b308d429aa17d8c0ea60264a3","controlGroup":"/user.slice/user-1000.slice/user@1000.service/app.slice/mosaic-chat-ca660028b2d51d3e4ca6fa194.scope"} + at TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:796:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } + +test at packages/conversation/tests/cohort.test.mjs:855:1 +✖ R4: after a normal engine exit the scope stays until a proven force stop releases it (10743.860597ms) + Error: timed out waiting for the release + at until (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:869:5) + 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/queue-50-review/r1/mut/norel-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/norel-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/norel-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/norelclose-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/norelclose-conversation.txt new file mode 100644 index 00000000..65730348 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/norelclose-conversation.txt @@ -0,0 +1,196 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (549.769276ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (183.832441ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (171.078605ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (550.102512ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (466.007231ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (272.268952ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (262.276804ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (243.861149ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (3.250658ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (13950.015565ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (44042.326088ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (404.17413ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (162.613114ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (511.703889ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (323.433367ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (422.41534ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (207.13533ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (478.974948ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (551.504938ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1175.951132ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (295.599116ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (123.528054ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (215.900544ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.331908ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.119151ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.748078ms) +✔ no shim from this file's tests is left running for the after hook (24.636913ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1314.738715ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (598.452181ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (692.138771ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2938.209222ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (627.601475ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (873.322401ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (839.452405ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5526.357229ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1129.148201ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (1127.737778ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (529.247992ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (887.403115ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (558.597602ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (934.096015ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3383.067677ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (47.709219ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (592.703545ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (353.427654ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (93.60854ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (740.77988ms) +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (819.411715ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1925.429506ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (1005.792931ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (448.140958ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (320.187336ms) +✔ no shim from this file's tests is left running for the after hook (24.78178ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (679.591127ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (533.221819ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (255.634857ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (352.961262ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (257.62546ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.421183ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (269.4411ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (338.499728ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (264.859211ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (278.887695ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (260.139551ms) +✔ 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 (258.33505ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.966316ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (254.533917ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (279.900767ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (395.124817ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (448.745428ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.402114ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.259207ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.314474ms) +✔ 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.500785ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.097398ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.495254ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.797253ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.468983ms) +✔ 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.348541ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.109974ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (229.555437ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (964.431602ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (412.139275ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1041.323053ms) +✔ H4: self-takeover is refused (243.288037ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (580.46802ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1378.017816ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (527.766553ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1228.54517ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (476.617114ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (246.871324ms) +✔ H13: a retry with the same request ID and different text is refused (247.015852ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (781.859336ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (320.385825ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (263.893093ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1056.487335ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (431.711242ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (985.215428ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (239.110923ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (405.514807ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.59531ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (811.725563ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (668.708387ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (867.311161ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3541.272093ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (734.161313ms) +✔ no shim from this file's tests is left running for the after hook (35.901282ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.588807ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.072726ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.411299ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.92073ms) +✔ F1: an unreadable fork is never merged into another branch's history (2.024187ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (3.544249ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (1.366745ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (2.067544ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (5.9044ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.755862ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (8.718923ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (9.2026ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (9.481285ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.301792ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.267328ms) +✔ F9: registrations never add or redirect a root (1.873746ms) +✔ F10: a header cwd naming another project is refused (4.416567ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.769206ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.308183ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.068615ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.374725ms) +✔ F13: compaction is a marker in place, then the retained content (0.798119ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (585.33114ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.213778ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (15.711101ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.691723ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.24185ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.301964ms) +✔ every page and cursor is a valid CHAT-01 record (824.23661ms) +✔ the engine pin holds for the installed package (2.511902ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (305.937194ms) +✔ 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) (587.730228ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (282.523964ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (1315.855797ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (553.236509ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (303.895555ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.884309ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (454.595018ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (251.917455ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (374.217356ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (593.655539ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2003.021059ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (428.78533ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (421.379902ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (277.127749ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (295.792929ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (470.222624ms) +✔ N10: fake conformance (32.724154ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (597.907516ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (353.272679ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (346.004684ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (425.822733ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (449.275563ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (432.897328ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (266.807496ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (467.001478ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (944.08151ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (708.360088ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (800.573429ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (309.904989ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (249.235737ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (356.633631ms) +✔ 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 (348.562061ms) +✔ 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 (346.839073ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 170 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 69745.158869 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:784:1 +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (819.411715ms) + AssertionError [ERR_ASSERTION]: {"loadState":"loaded","activeState":"active","invocationId":"57f1c8ae68c94304aa7231301f396395","controlGroup":"/user.slice/user-1000.slice/user@1000.service/app.slice/mosaic-chat-cdf9359d8fbf3a52888767148.scope"} + at TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:796:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-50-review/r1/mut/norelclose-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/norelclose-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/norelclose-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/norelstop-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/norelstop-conversation.txt new file mode 100644 index 00000000..bfac6b3f --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/norelstop-conversation.txt @@ -0,0 +1,197 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (272.264638ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (46.329468ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (29.629157ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (455.910736ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (2765.552141ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (381.851999ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (234.91902ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (240.827066ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.405225ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (17091.443612ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (44580.219596ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (2118.993554ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (918.667986ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (1230.632529ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (424.058027ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (438.232889ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (190.130178ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (327.538779ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (645.070589ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1518.943207ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (293.724772ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (132.776026ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (94.056128ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.136685ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.061635ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.759315ms) +✔ no shim from this file's tests is left running for the after hook (25.163013ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (849.774057ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (3011.947819ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (645.23619ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3084.539394ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (761.564834ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (797.010489ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (737.196813ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5888.672791ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1025.526708ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (3073.695765ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (647.654801ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (947.863184ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (465.589036ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (737.958093ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3332.063326ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (39.969918ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (452.785563ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (425.873865ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (268.671198ms) +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (8674.808724ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (674.743143ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1490.23582ms) +✖ R4: after a normal engine exit the scope stays until a proven force stop releases it (8856.338719ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (515.110429ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (139.672464ms) +✔ no shim from this file's tests is left running for the after hook (26.092152ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (465.73134ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (257.543969ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (237.447584ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (1128.448536ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (1841.007765ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.421681ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (357.53504ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (338.140709ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (265.05974ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (358.266257ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (255.276237ms) +✔ 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 (270.731886ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.078345ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (247.03924ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (278.379754ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (327.580008ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (249.016957ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.448978ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.280753ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.356273ms) +✔ 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.552806ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.270809ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.531662ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.829284ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.452394ms) +✔ 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.341027ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.111466ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (254.61014ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (328.058872ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (348.940465ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (3456.102016ms) +✔ H4: self-takeover is refused (237.915598ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (669.9985ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1328.085776ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (291.439922ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1367.806925ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (371.710321ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (227.526813ms) +✔ H13: a retry with the same request ID and different text is refused (240.804604ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (1021.792562ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (331.76346ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (229.639927ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1043.206581ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (470.756353ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1367.437572ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (275.008156ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (354.824834ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.740873ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (769.496129ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (768.18178ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (822.658509ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3460.258012ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (775.97791ms) +✔ no shim from this file's tests is left running for the after hook (29.483937ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (10.400479ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.937108ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.996079ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (4.229465ms) +✔ F1: an unreadable fork is never merged into another branch's history (2.183748ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (3.621972ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (1.023009ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.532727ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.70294ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.600597ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.203509ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.910249ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (10.371946ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.384324ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.742271ms) +✔ F9: registrations never add or redirect a root (1.704415ms) +✔ F10: a header cwd naming another project is refused (3.93946ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.738278ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.512252ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.638599ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.659874ms) +✔ F13: compaction is a marker in place, then the retained content (0.824867ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (601.165424ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.562248ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.484325ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.304021ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.048542ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.891326ms) +✔ every page and cursor is a valid CHAT-01 record (909.191684ms) +✔ the engine pin holds for the installed package (2.587502ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (313.885817ms) +✔ 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) (615.161295ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (302.503555ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (812.346666ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (2501.404705ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (836.894821ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.854475ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (413.248551ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (241.83536ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (401.540736ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (637.727834ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2037.89795ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (238.605688ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (323.516026ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (198.047343ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (248.830916ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (707.403322ms) +✔ N10: fake conformance (33.353164ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (580.178916ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (316.306497ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (312.625659ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (439.691488ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (449.887357ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (481.666056ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (332.680695ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (717.400644ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (990.328961ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (701.020369ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (704.194245ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (269.707647ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (240.292236ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (206.512511ms) +✔ 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 (247.664468ms) +✔ 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 (251.334814ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 169 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 79035.778078 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:767:1 +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (8674.808724ms) + Error: timed out waiting for the release + at until (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:774:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) + +test at packages/conversation/tests/cohort.test.mjs:855:1 +✖ R4: after a normal engine exit the scope stays until a proven force stop releases it (8856.338719ms) + Error: timed out waiting for the release + at until (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:869:5) + 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/queue-50-review/r1/mut/norelstop-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/norelstop-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/norelstop-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/nowait-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/nowait-conversation.txt new file mode 100644 index 00000000..eb68958c --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/nowait-conversation.txt @@ -0,0 +1,196 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (252.76282ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.556357ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (23.056491ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (709.066069ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (487.476367ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (290.628914ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (352.460511ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (229.582168ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.587924ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (13041.514028ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (40470.164105ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (280.637101ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (160.162383ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (791.514986ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (367.135166ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (548.575282ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (169.391239ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (266.952232ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (499.528577ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1606.641257ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (392.417304ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (110.328189ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (88.7047ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.055344ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.136465ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.807747ms) +✔ no shim from this file's tests is left running for the after hook (25.778535ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1097.941753ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (618.425444ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (959.312007ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3012.796016ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (794.482542ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (673.800067ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (873.414434ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5494.475609ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (772.209986ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (754.563319ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (596.916384ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (681.141782ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (332.761399ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (508.513527ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3263.607563ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (24.114853ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (387.960821ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (415.559143ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (243.618098ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (967.576995ms) +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (674.52603ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1353.719258ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (738.744565ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (446.962573ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (118.674979ms) +✔ no shim from this file's tests is left running for the after hook (25.510928ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (257.589105ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (231.993134ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (484.002995ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (380.834001ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (289.182872ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.394518ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (247.512217ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (464.148543ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (244.096816ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (352.441912ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (397.702007ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (212.915926ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.075776ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (219.605725ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (240.216628ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (281.069388ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (229.441098ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.481992ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.338608ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.39528ms) +✔ 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.606777ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.379216ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.552502ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.818644ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.413633ms) +✔ 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.38506ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.120694ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (266.518531ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (286.026276ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (581.728097ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1181.443655ms) +✔ H4: self-takeover is refused (231.583804ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (809.591093ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1121.925304ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (257.123691ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (650.332055ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (311.397017ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (213.879261ms) +✔ H13: a retry with the same request ID and different text is refused (210.997592ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (635.224587ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (271.869765ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (303.468838ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1275.205501ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (463.867075ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (961.835066ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (206.880232ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (333.570665ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.663248ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (693.643989ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (644.789588ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (735.74316ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2947.181453ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (646.774776ms) +✔ no shim from this file's tests is left running for the after hook (25.490174ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.485981ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.037302ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.054753ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.891253ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.761099ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.178947ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.875889ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.419607ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.623648ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.828658ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.818748ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.671285ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.615005ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.774748ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.66851ms) +✔ F9: registrations never add or redirect a root (1.67955ms) +✔ F10: a header cwd naming another project is refused (3.746602ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.683687ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.930784ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.998835ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.373597ms) +✔ F13: compaction is a marker in place, then the retained content (0.780304ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (590.588393ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.299081ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.873606ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.69062ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.297448ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.291493ms) +✔ every page and cursor is a valid CHAT-01 record (889.67595ms) +✔ the engine pin holds for the installed package (2.152405ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (295.814043ms) +✔ 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) (601.847147ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (296.510831ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (567.046979ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (822.431994ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (320.541931ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (19.956625ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (595.244054ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (221.753144ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (455.502753ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (684.586084ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1892.468337ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (201.693573ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (310.100779ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (218.480251ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (244.626252ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (322.809538ms) +✔ N10: fake conformance (31.769008ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (585.507375ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (450.251226ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (444.818254ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (430.114795ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (554.464333ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (425.659594ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (209.813615ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (361.98249ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (672.938897ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (619.23191ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (541.171274ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (214.004046ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (191.118767ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (203.283951ms) +✔ 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 (196.919531ms) +✔ 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 (220.471721ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 170 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 65439.044393 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:784:1 +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (674.52603ms) + AssertionError [ERR_ASSERTION]: {"loadState":"loaded","activeState":"active","invocationId":"903d188abad8438eb75b7267d831f154","controlGroup":"/user.slice/user-1000.slice/user@1000.service/app.slice/mosaic-chat-c6faf7cc2470ee281a3187356.scope"} + at TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:796:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-50-review/r1/mut/nowait-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/nowait-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/nowait-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/mut/run.sh b/agents/darkwing/work/queue-50-review/r1/mut/run.sh new file mode 100755 index 00000000..c87f5ed4 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/run.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# run.sh : mutate, run the conversation suite, list and kill shims left under TMPDIR, restore from backups. +m=$1 +export TMPDIR=~/darkwing-scratch/r50a/tmp/mut-$m DOCKER_HOST=unix:///nonexistent.sock +mkdir -p $TMPDIR +M=~/darkwing-scratch/r50a/mut; cd ~/darkwing-scratch/r50a/wt2 +files=$(python3 -I $M/mutate.py $m --files) +for f in $files; do cp -p $f $f.mutbak; done +if python3 -I $M/mutate.py $m > /dev/null; then + node --test "packages/conversation/tests/*.test.mjs" > $M/$m-conversation.txt 2>&1; e=$? +else e=mutate-failed; fi +left=$(ps -eo pid=,args= | grep -F "$TMPDIR/" | grep -F /shim.mjs | grep -v grep) +n=$(printf '%s' "$left" | grep -c .) +echo "$left" > $M/$m-shims-left.txt +printf '%s\n' "$left" | while read pid rest; do + [ -n "$pid" ] || continue + u=$(tr '/' '\n' < /proc/$pid/cgroup | grep '^mosaic-chat-.*\.scope$') + [ -n "$u" ] && systemctl --user kill --signal=SIGKILL "$u" + kill -9 $pid 2>/dev/null +done +for f in $files; do mv $f.mutbak $f; done +echo "$m rc $e $(grep -E '^ℹ (pass|fail)' $M/$m-conversation.txt | tr '\n' ' ') shims-left $n" >> $M/summary.txt diff --git a/agents/darkwing/work/queue-50-review/r1/mut/summary.txt b/agents/darkwing/work/queue-50-review/r1/mut/summary.txt new file mode 100644 index 00000000..a5b1da2d --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/summary.txt @@ -0,0 +1,14 @@ +norel rc 1 ℹ pass 168 ℹ fail 3 shims-left 0 +norelstop rc 1 ℹ pass 169 ℹ fail 2 shims-left 0 +norelclose rc 1 ℹ pass 170 ℹ fail 1 shims-left 0 +nowait rc 1 ℹ pass 170 ℹ fail 1 shims-left 0 +wait0 rc 1 ℹ pass 169 ℹ fail 2 shims-left 0 +noguard rc 0 ℹ pass 171 ℹ fail 0 shims-left 0 +closenoproof rc 0 ℹ pass 171 ℹ fail 0 shims-left 0 +nopush rc 0 ℹ pass 171 ℹ fail 0 shims-left 0 +noevid rc 1 ℹ pass 168 ℹ fail 3 shims-left 0 +invnull rc 0 ℹ pass 171 ℹ fail 0 shims-left 0 +nohello rc 1 ℹ pass 170 ℹ fail 1 shims-left 0 +early rc 1 ℹ pass 169 ℹ fail 2 shims-left 0 +n2 rc 1 ℹ pass 170 ℹ fail 1 shims-left 0 +DONE diff --git a/agents/darkwing/work/queue-50-review/r1/mut/wait0-conversation.txt b/agents/darkwing/work/queue-50-review/r1/mut/wait0-conversation.txt new file mode 100644 index 00000000..bceec390 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/wait0-conversation.txt @@ -0,0 +1,219 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (290.086066ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (44.957773ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (23.131115ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (637.077785ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (445.374396ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (247.078742ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (193.718414ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (198.117338ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.57093ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11582.486741ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (36849.564432ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (344.545779ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (157.940285ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (496.286315ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (307.145767ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (453.001185ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (118.883569ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (235.074309ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (359.521897ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1109.483584ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (243.987603ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (92.123869ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (71.633006ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.100515ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.112855ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.73842ms) +✔ no shim from this file's tests is left running for the after hook (24.318472ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1025.576574ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (498.159768ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (805.312115ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2776.626878ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (482.958173ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (779.561732ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (487.064758ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5459.394056ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (702.900055ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (708.316616ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (508.039153ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (662.64577ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (387.315208ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (475.764592ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3341.78803ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (28.977014ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (411.872051ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (492.194212ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.416036ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (536.463311ms) +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (739.590671ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1777.090727ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (819.661307ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (446.507505ms) +✖ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (330.896923ms) +✔ no shim from this file's tests is left running for the after hook (19.672169ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (287.978301ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (476.284741ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (241.431675ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (312.606888ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (215.419658ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.409586ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (204.220135ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (296.143242ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (211.891745ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (198.009772ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (206.011814ms) +✔ 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.006331ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.117292ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (210.618805ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (222.467738ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (259.265241ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (206.623104ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.802571ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.265097ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.324645ms) +✔ 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.476686ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.042282ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.467595ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.676827ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.353573ms) +✔ 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.329601ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.098485ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (225.090887ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (361.002007ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (553.400747ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (864.284679ms) +✔ H4: self-takeover is refused (186.534216ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (441.4328ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1052.373092ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (226.552137ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (657.25826ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (307.357098ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (196.952325ms) +✔ H13: a retry with the same request ID and different text is refused (195.873936ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (585.483429ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (280.739277ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (163.492716ms) +✔ 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.00536ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (332.510684ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (733.367004ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (191.835028ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (326.708222ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.607798ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (672.085374ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (572.542485ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (827.570551ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2943.56165ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (687.243041ms) +✔ no shim from this file's tests is left running for the after hook (25.284081ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (9.428768ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.971496ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (3.022339ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (4.371745ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.875348ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.187135ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (1.004779ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.326717ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.566749ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.541763ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.332726ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.875969ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.916127ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.846235ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.626777ms) +✔ F9: registrations never add or redirect a root (1.544104ms) +✔ F10: a header cwd naming another project is refused (3.56537ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.656435ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.898825ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.413419ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.416841ms) +✔ F13: compaction is a marker in place, then the retained content (0.70107ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (562.113682ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.508955ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (6.985134ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.234355ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.069822ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.892989ms) +✔ every page and cursor is a valid CHAT-01 record (863.91415ms) +✔ the engine pin holds for the installed package (2.023096ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (277.982515ms) +✔ 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) (619.256472ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (294.766141ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (850.613698ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (490.164117ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (253.599078ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.468707ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (351.919672ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (195.160502ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (299.696553ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (460.785199ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1903.747276ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (196.499786ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (279.099894ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (201.04823ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (226.114075ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (350.848844ms) +✔ N10: fake conformance (33.049926ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (418.738876ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (267.052895ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (268.030311ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (344.129349ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (353.03057ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (339.672487ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (200.445622ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (331.449206ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (716.672109ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (824.094311ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (556.738754ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (220.09405ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (183.984904ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (201.017789ms) +✔ 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 (171.460445ms) +✔ 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 (198.694677ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 169 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 58592.66983 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:784:1 +✖ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (739.590671ms) + AssertionError [ERR_ASSERTION]: {"loadState":"not-found","activeState":"inactive","invocationId":null,"controlGroup":null} + at TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:796:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } + +test at packages/conversation/tests/cohort.test.mjs:912:1 +✖ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (330.896923ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + { + outcome: 'released', + + unit: 'still listed' + - unit: 'absent' + } + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r50a/wt2/packages/conversation/tests/cohort.test.mjs:927: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: { outcome: 'released', unit: 'still listed' }, + expected: { outcome: 'released', unit: 'absent' }, + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-50-review/r1/mut/wait0-shims-left.txt b/agents/darkwing/work/queue-50-review/r1/mut/wait0-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/mut/wait0-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-50-review/r1/out/node-conversation.txt b/agents/darkwing/work/queue-50-review/r1/out/node-conversation.txt new file mode 100644 index 00000000..3005d53e --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/out/node-conversation.txt @@ -0,0 +1,179 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (263.835627ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.922714ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.082087ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (344.995ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (393.771992ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (236.263447ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (195.450653ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (203.896544ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.295169ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (13357.324433ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (41363.212166ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (378.679871ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (218.50041ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (626.283151ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (395.139339ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (522.985147ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (172.925678ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (329.404447ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (564.288297ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1650.444223ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (570.416048ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (142.783122ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (96.074527ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.734535ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.221374ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.03125ms) +✔ no shim from this file's tests is left running for the after hook (27.882076ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (986.339812ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (435.76874ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (787.726583ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2967.369504ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (671.674039ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (786.857027ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (752.58161ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (6071.418813ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1108.339907ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (752.39347ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (701.090707ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (838.697192ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (451.857426ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (621.992132ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3265.179691ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (61.608754ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (505.268298ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (315.958297ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.303955ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (425.121302ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (751.510622ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1232.957678ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (725.078506ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (451.512578ms) +✔ R6: releaseCohort releases nothing for another invocation ID or a cohort without a scope (368.639222ms) +✔ no shim from this file's tests is left running for the after hook (26.198722ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (272.764902ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (202.783521ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (192.04678ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (292.204887ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (185.35431ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.428619ms) +✔ 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.490273ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (298.840349ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (212.323776ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (192.627695ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (204.735193ms) +✔ 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.736293ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.677071ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (189.14845ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (208.784609ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (239.169678ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (189.290774ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.359494ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.242336ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.291083ms) +✔ 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.462721ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.936244ms) +✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.441437ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.7378ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.881127ms) +✔ 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.323355ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.106354ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (245.401454ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (316.698551ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (281.770762ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (780.661136ms) +✔ H4: self-takeover is refused (197.794852ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (448.558157ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (952.146653ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (211.343989ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (526.32941ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (256.594413ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (118.283417ms) +✔ H13: a retry with the same request ID and different text is refused (127.511755ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (673.855095ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (273.499143ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (195.757343ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (850.768601ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (359.42882ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (792.521247ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (220.678937ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (439.158182ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.714627ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (702.439075ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (625.672005ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (854.42331ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3712.152072ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (681.193471ms) +✔ no shim from this file's tests is left running for the after hook (29.935584ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.241359ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.707129ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.623821ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.378358ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.457475ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.752772ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.696341ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.166625ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.320688ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.129842ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.246193ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.676532ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (8.073628ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.729989ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.25719ms) +✔ F9: registrations never add or redirect a root (1.81179ms) +✔ F10: a header cwd naming another project is refused (4.118106ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.755575ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.965672ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.271547ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.430677ms) +✔ F13: compaction is a marker in place, then the retained content (0.804749ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (592.272297ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.739229ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.569496ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.223314ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.043444ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.955125ms) +✔ every page and cursor is a valid CHAT-01 record (960.991365ms) +✔ the engine pin holds for the installed package (2.741243ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (262.955034ms) +✔ 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) (546.10653ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (253.708654ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (534.959461ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (445.166724ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (219.360271ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.20532ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (344.484361ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (201.035481ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (297.93017ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (447.556561ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1947.319985ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (187.01804ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (270.242951ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (176.771626ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (167.551553ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (385.524441ms) +✔ N10: fake conformance (32.951805ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (426.742908ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (260.482057ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (291.464907ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (393.617956ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (419.744423ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (476.524004ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (153.543702ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (391.037765ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (1006.203048ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (717.42288ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (1178.015027ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (369.810097ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (246.027664ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (265.937379ms) +✔ 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 (168.283323ms) +✔ 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 (267.538929ms) +ℹ tests 171 +ℹ suites 0 +ℹ pass 171 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 65892.627684 diff --git a/agents/darkwing/work/queue-50-review/r1/out/node-webui.txt b/agents/darkwing/work/queue-50-review/r1/out/node-webui.txt new file mode 100644 index 00000000..59ff734c --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/out/node-webui.txt @@ -0,0 +1,31 @@ +✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5299.08924ms) +Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286} +✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6116.773655ms) +✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (6341.081545ms) +✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (183.35217ms) +✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (43.765615ms) +✔ bus refusals map to statuses and never carry a path or a stack (20.4502ms) +✔ humanCall speaks the human transport protocol without blocking the server (1823.840292ms) +✔ busReader takes --business, else the live bus host, else refuses (207.085073ms) +✔ humanCall kills a transport that runs past its timeout (537.55544ms) +✔ serve refuses a --business value that is not a business id (229.139439ms) +✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5555.816009ms) +✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2267.152032ms) +✔ conversation view: a newer session with no readable history keeps the marker (1320.558735ms) +✔ conversation view: seats without history say so and offer no reply (857.529463ms) +✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5729.063679ms) +✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (55107.601574ms) +✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5154.387337ms) +✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23649.947578ms) +✔ loopback host and board origin fail closed (9.614816ms) +✔ real board fixture passes through WebUI; assets and isolated seen/reply work (88.56432ms) +✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (43.061019ms) +✔ unreachable board reports URL; CLI rejects unsupported options (451.914656ms) +ℹ tests 22 +ℹ suites 0 +ℹ pass 22 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 55406.577069 diff --git a/agents/darkwing/work/queue-50-review/r1/out/summary.txt b/agents/darkwing/work/queue-50-review/r1/out/summary.txt new file mode 100644 index 00000000..491a461d --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/out/summary.txt @@ -0,0 +1,14 @@ +start 2026-10-10T04:51:00Z shims=0 units=0 +node-conversation exit=0 ℹ pass 171 ℹ fail 0 shims-left=0 units=3 +node-webui exit=0 ℹ pass 22 ℹ fail 0 shims-left=2 units=3 +test-auth exit=0 selftest: 15 passed, 0 failed +test-conductor exit=0 selftest: 17 passed, 0 failed +test-config exit=0 selftest: 24 passed, 0 failed +test-discord exit=0 discord suite: 66 passed, 0 failed +test-extension-package exit=0 extension package selftest: 18 passed, 0 failed +test-foundation exit=0 selftest: 44 passed, 0 failed +test-queue exit=0 queue suite: 27 passed, 0 failed +test-release exit=0 selftest: 4 passed, 0 failed +test-task exit=1 selftest: 26 passed, 2 failed +end 2026-10-10T04:54:51Z shims-left=1 units=2 +DONE diff --git a/agents/darkwing/work/queue-50-review/r1/out/test-auth.txt b/agents/darkwing/work/queue-50-review/r1/out/test-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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/queue-50-review/r1/out/test-conductor.txt b/agents/darkwing/work/queue-50-review/r1/out/test-conductor.txt new file mode 100644 index 00000000..62afc513 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/out/test-conductor.txt @@ -0,0 +1,23 @@ +On branch refactor +Your branch is up to date with 'origin/refactor'. + +nothing to commit, working tree clean +OK dry-run: allowed change, exit 0, nothing committed (exit 0) +OK dry-run committed nothing +OK apply: allowed change exits 0 (exit 0) +OK apply: attribution in commit subject +OK apply: target tree clean after commit +OK disallowed path refused (exit 1) +OK disallowed path: target untouched +OK syntax gate refused broken .mjs (exit 1) +OK syntax gate: target untouched +OK suite failure refused (exit 1) +OK suite failure: target reverted to clean +OK disabled policy refused (exit 2) +OK disabled policy: target untouched +OK failed run refused (exit 1) +OK failed run: target untouched +OK missing run exits 4 (exit 4) +OK invalid policy exits 2 (exit 2) + +selftest: 17 passed, 0 failed diff --git a/agents/darkwing/work/queue-50-review/r1/out/test-config.txt b/agents/darkwing/work/queue-50-review/r1/out/test-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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/queue-50-review/r1/out/test-discord.txt b/agents/darkwing/work/queue-50-review/r1/out/test-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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/queue-50-review/r1/out/test-extension-package.txt b/agents/darkwing/work/queue-50-review/r1/out/test-extension-package.txt new file mode 100644 index 00000000..e66126b2 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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: 176540 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/queue-50-review/r1/out/test-foundation.txt b/agents/darkwing/work/queue-50-review/r1/out/test-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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/queue-50-review/r1/out/test-queue.txt b/agents/darkwing/work/queue-50-review/r1/out/test-queue.txt new file mode 100644 index 00000000..fa3e74d5 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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/r50a/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/queue-50-review/r1/out/test-release-docker.txt b/agents/darkwing/work/queue-50-review/r1/out/test-release-docker.txt new file mode 100644 index 00000000..7202b85d --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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/queue-50-review/r1/out/test-release.txt b/agents/darkwing/work/queue-50-review/r1/out/test-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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/queue-50-review/r1/out/test-task.txt b/agents/darkwing/work/queue-50-review/r1/out/test-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/r1/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/queue-50-review/review-r1.md b/agents/darkwing/work/queue-50-review/review-r1.md new file mode 100644 index 00000000..15dc85f4 --- /dev/null +++ b/agents/darkwing/work/queue-50-review/review-r1.md @@ -0,0 +1,175 @@ +# Row 50, cohort scope release after a force stop, round 1 review (Darkwing) + +Issue #1536, request comment 27051, packet comment 27052, queue revs 285 +and 286 (`a400f521`). Brief: `docs/plans/2026-10-10_cohort-scope-release.md`, +section "Cohort scope release after a force stop". Base `16a8d038`; I ran +it on `a400f521`, which adds only the two queue files. Candidate manifest +sha256 +`375594fc64b3e42be17a2b5f8983c0925eea37c928b1e8903640f4723c19bab9`, +8 files: `cohort.mjs`, `controller.mjs`, four test files, the README and +Dewey's `evidence.md`. `shim.mjs` is unchanged. + +Verdict: **approve**, with one point Sage has to rule on. The release +follows a proof in every path I could build: the force stop calls it only +after the claim records `stopped`, close only for a binding that +`#stopped()` accepts, and `releaseCohort` refuses a cohort that isn't a +scope or a shim that answers for another invocation. The shim itself +refuses `release` unless `engine` reads `populated 0`, so a release can't +drop live members. 13 mutants: 9 killed, 4 survive, and none of the +survivors is a gap I'd hold the row for. The one open point is the normal +engine exit. The brief says to fix it in this row if it leaks. It leaks, +and Dewey didn't fix it. I agree it can't be fixed here, for the reason +below, but that's Sage's call, not mine. + +## Method + +- Detached worktree at `a400f521`, the seven candidate files copied from a + snapshot of the canonical tree, `evidence.md` alongside, then + `sha256sum -c`: 8 OK. A second worktree for the mutants; its seven files + check OK after all runs (`r1/mut/manifest-after.txt`). +- I read the diff in full, `shim.mjs` (`release` at lines 160-172), + `#forceStop`, `#serveOrphan`, `#onEnd`/`#transport`/`#uncertain`, + `#recover` and `close()` in `controller.mjs`, and R1-R6. +- 13 mutants (`r1/mut/mutate.py`, `run.sh`), the whole conversation suite + each, own TMPDIR each. After each run the runner lists any shim left + under that TMPDIR, kills its `mosaic-chat-*` scope and the PID, and + restores the files. No mutant left a shim. + +Node v26.8.1, `TMPDIR=~/darkwing-scratch/r50a/tmp`, `gate.sh` with +`DOCKER_HOST=unix:///nonexistent.sock`. + +## Suites + +| Suite | Result | +|---|---| +| conversation | 171/0 | +| webui | 22/0 | +| test-auth | 15/0 | +| test-conductor | 17/0 | +| test-config | 24/0 | +| test-discord | 66/0 | +| test-extension-package | 18/0 | +| test-foundation | 44/0 | +| test-queue | 27/0 | +| test-release | 4/0 without Docker, 14/0 with it (`test-release-docker.txt`) | +| test-task | 26/2 | + +The two `test-task` failures are "user recall run succeeds" and "recalled +user name", the live worker check that needs Docker and a model call, as in +my row 41 and row 47 gates. Dewey's 98/0 ran them. + +The shim and unit counts in `r1/out/summary.txt` aren't clean numbers. +Filbert's row 50 runs were live on the same user manager (the three units +after the conversation suite were in `~/filbert-scratch`), and my mutants +started under `tmp/mut-*` while the gate was still running, inside the +gate's `tmp` prefix. Checked directly at the end: no shim under my gate's +`tmp/chat03-*`, and no `mosaic-chat-*` unit whose command line names +`darkwing-scratch`. + +## The change + +`releaseCohort` (`cohort.mjs`) checks the kind, then `hello`, then the +invocation ID, then sends `release` and polls the unit for up to 3 s. The +order is right: every refusal comes before `release`, so an `unavailable` +result never means a half-done release. + +`#releaseScope` (`controller.mjs`) acts only on a claim record with state +`stopped`, a `cohortProof` and a shim, and memoizes one promise per claim +ID. The force stop takes `proven = this.claim` after `#claimFinish`, so it +releases the claim it proved, even if the claim changes while the +`scope-release` barrier holds. The force stop's reply is the +`force-stop-fenced` outcome with the stop as its `after`, so the release +doesn't delay the client's reply. Every `fail()` in `#forceStop` returns +before the release, which R3 covers for both an unavailable outcome and a +rejected proof. + +Close calls it when `this.b.state === "stopped"` and `#stopped()` accepts +the stop. After `recover` and `launch` the binding is no longer `stopped`, +so close releases only the stopped binding's own claim. + +## A normal engine exit (for Sage) + +The brief: "First check whether a normal engine exit (no force stop) also +leaves the shim. If it does, fix that path in the same row; if it doesn't, +say why in the packet." + +It does leave the shim. R4 shows it: after the fake engine exits 0 the +binding is `uncertain`, the unit is active and the shim reports +`engineExit {code: 0}`. Dewey didn't fix it, and gives reasons in the +packet. I checked them against the code: + +- A release with no recorded stop would leave the claim `uncertain` with + its scope collected. The only way out of `uncertain` is a confirmed + force stop (`#serveOrphan`'s comment, line 388), and that stop reads the + cohort through the shim. With the shim gone it ends `unavailable`, and + the seat stays `uncertain` for good. That's worse than the leak. +- The fix that keeps the invariant is to prove the empty cohort on EOF and + record `stopped`, which is what R4's force stop does. Doing that without + a client's confirmation changes the force-stop and confirmation protocol + (K6, confirmations bind target, operation and stop). The brief puts the + force-stop phases and the claim protocol out of scope. + +So the brief asks for two things that can't both hold. I agree with +Dewey's reading, and the row still strictly reduces leaks. But the +remaining leak is likely the larger one: every engine that exits on its own +leaves a `mosaic-chat-*` scope and an idle node shim until someone runs a +confirmed force stop or the user manager restarts. My recommendation: +accept the row as is and open a follow-up for an engine exit, either a +controller-run proof on EOF (no confirmation needed when the cohort is +already empty) or a stated decision that the operator's force stop is the +cleanup. + +## Mutants + +| Mutant | Change | Conversation | Result | +|---|---|---|---| +| norel | neither the force stop nor close releases | 168/3 | killed: R1, R2, R4 | +| norelstop | the force stop doesn't release | 169/2 | killed: R1, R4 | +| norelclose | close doesn't release | 170/1 | killed: R2 | +| nowait | `releaseCohort` reports `absent` without looking | 170/1 | killed: R2 (unit still active right after close) | +| wait0 | `releaseCohort` doesn't wait for systemd | 169/2 | killed: R2, R6 | +| noevid | no entry in `evidence.releases` | 168/3 | killed: R1, R2, R4 | +| nohello | no `hello` and no invocation check before `release` | 170/1 | killed: R6 | +| early | the force stop releases before the claim records `stopped` | 169/2 | killed: R1, R4 (the guard refuses, nothing is released) | +| n2 | `killShims` signals any unit | 170/1 | killed: R5 | +| noguard | `#releaseScope` checks only the shim; callers unchanged | 171/0 | survives: both callers already gate on a proven stop | +| closenoproof | close drops the `#stopped()` check | 171/0 | survives: the inner guard still needs `stopped` and a `cohortProof` | +| invnull | `releaseCohort` drops the null-ID check | 171/0 | survives: a real shim always reports an ID, so `null !== id` refuses anyway | +| nopush | the release entry isn't pushed to observers | 171/0 | survives: no test reads the pushed evidence | + +`noguard` and `closenoproof` are two guards each covering for the other. +Dewey's `relfail` and `closeany` weaken both at once and are killed by R3, +so the pair is tested. `invnull` matters only for a shim that answers with +no invocation ID, which `shim.mjs` can't produce today. `nopush` is the one +real hole, and a small one: the board and terminal would get no release +entry, and nothing would fail. + +`early` is worth a line. With the release moved before `#claimFinish`, the +guard sees `stopping` and refuses, so the stop never releases. That's the +guard working; R1 and R4 catch the missing release. + +## Notes (not blocking) + +1. `nopush` survives. One assertion that an observer sees + `{ kind: "evidence", evidence: { kind: "release", ... } }` in R1 would + close it. +2. `releaseCohort` polls `systemdUnits.lookup`, a `spawnSync` of + `systemctl show`, every 20 ms for up to 3 s on the controller's event + loop. One lookup takes about 4.5 ms here, so a unit that lingers costs + about a fifth of the loop for 3 s. It's the codebase's existing call and + the common case returns in one or two polls. An async lookup or a + longer interval would be enough if it ever shows up. +3. I agree with Dewey's two bounded follow-ups: the crash window between + recording `stopped` and the release (a restart classifies the pair + `stopped` and never releases), and `close({ killEngine: true })` sending + SIGKILL to a recorded PID after a proven stop. A release that came back + `unavailable` or `still listed` also has no retry once the binding moves + on after `recover`. That fits in the same follow-up as the crash + window. + +## Files + +- `r1/candidate-manifest.sha256`: copy of Dewey's. +- `r1/gate.sh`, `r1/out/`: suite runs and `summary.txt`. +- `r1/mut/`: mutant definitions, runner, per-mutant output and + shims-left lists, `summary.txt`, and the manifest check after the runs.