diff --git a/agents/darkwing/work/queue-51-review/r1/candidate-manifest.sha256 b/agents/darkwing/work/queue-51-review/r1/candidate-manifest.sha256 new file mode 100644 index 00000000..6d8bc38a --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/candidate-manifest.sha256 @@ -0,0 +1,5 @@ +a12797977726884c3a45da034d513e7868608b63f55299e7190cd0b88a718022 agents/dewey/work/queue-51/evidence.md +e3d4fadcbeebf0759f53521b58de10ef8c5c9e282e4894db1a99ac1b228046c1 packages/conversation/README.md +cec736c520a8c59008e5f75e7cfa5563915bdffae7221a688fa489c75be31826 packages/conversation/src/controller.mjs +d6f1662554eabf14c12624a76f5283989ae06d057165df32e6886eadc75b8c47 packages/conversation/tests/cohort.test.mjs +3409fcce6ebb0281b5e018983a005b0c23b8641ba69cd25ea8aec6c161870d3b packages/conversation/tests/ctrl-child.mjs diff --git a/agents/darkwing/work/queue-51-review/r1/gate.sh b/agents/darkwing/work/queue-51-review/r1/gate.sh new file mode 100755 index 00000000..20953567 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/gate.sh @@ -0,0 +1,19 @@ +#!/bin/bash +export TMPDIR=~/darkwing-scratch/r51a/tmp DOCKER_HOST=unix:///nonexistent.sock +cd ~/darkwing-scratch/r51a/wt +O=~/darkwing-scratch/r51a/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-51-review/r1/mut/closekill-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/closekill-conversation.txt new file mode 100644 index 00000000..80e1b7c2 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/closekill-conversation.txt @@ -0,0 +1,212 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (201.159062ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.809981ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.553174ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (588.737087ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (383.87052ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (234.847397ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (190.116715ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (187.42963ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.466124ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11410.496719ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38528.303106ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (269.052366ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (150.119212ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (664.019433ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (298.358696ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (357.970798ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (115.115609ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (213.6174ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (282.599766ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (734.796585ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (181.419818ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (89.441426ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (73.682744ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (5.238319ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.076697ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.01279ms) +✔ no shim from this file's tests is left running for the after hook (25.642294ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1036.440187ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (446.118141ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (582.009565ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2751.57711ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (500.143301ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (652.40386ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (512.750695ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5630.073785ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (797.23316ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (711.849196ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (474.261326ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (767.749793ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (355.045342ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (557.283537ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3272.333441ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (41.811034ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (395.84717ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (293.573041ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (64.171473ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (420.138448ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (542.651947ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1463.226281ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (700.334185ms) +✔ 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) (486.500695ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (166.788283ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2774.595191ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (582.627534ms) +✔ R9: a release that ended unavailable is tried again on recover (636.144251ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (419.309ms) +✖ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (702.582403ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (310.529304ms) +✔ no shim from this file's tests is left running for the after hook (23.746226ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (211.147826ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (219.057011ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (409.739309ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (270.402872ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (184.844118ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.456216ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (190.321281ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (292.256578ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (200.39001ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (177.701512ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (193.315153ms) +✔ 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 (196.772346ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.043588ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (196.167339ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (215.601034ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (244.85706ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (200.389439ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.393317ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.257331ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.333628ms) +✔ 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.477664ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.165933ms) +✔ 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.489487ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.722425ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.351628ms) +✔ 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.318323ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.095704ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (216.667856ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (476.083948ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (281.839154ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (776.30359ms) +✔ H4: self-takeover is refused (188.397059ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (410.004116ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (978.233273ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (207.356569ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (627.080293ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (331.793405ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (189.895805ms) +✔ H13: a retry with the same request ID and different text is refused (170.25308ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (596.847649ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (275.319197ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (227.725678ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (845.226867ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (444.195556ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (912.31796ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (197.283195ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (360.379052ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.623626ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (681.402421ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (625.251805ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (744.590352ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2941.495263ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (632.180302ms) +✔ no shim from this file's tests is left running for the after hook (26.167559ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.918104ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.605983ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.70974ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.366699ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.318913ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.875141ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.768365ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.153866ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.937019ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.113961ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.91126ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.223319ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.444607ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.451835ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.792243ms) +✔ F9: registrations never add or redirect a root (2.028555ms) +✔ F10: a header cwd naming another project is refused (4.035554ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.728041ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.398372ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.038572ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.436865ms) +✔ F13: compaction is a marker in place, then the retained content (0.808794ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (510.79551ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.353657ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.4831ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.137788ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.004329ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.84461ms) +✔ every page and cursor is a valid CHAT-01 record (777.946536ms) +✔ the engine pin holds for the installed package (1.939678ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (278.277451ms) +✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (512.534106ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (253.8399ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (550.155524ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (594.699438ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (217.521684ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.079626ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (336.613156ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (189.509156ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (287.368558ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (439.913295ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1865.001004ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (152.216871ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (270.394326ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (183.040795ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (228.383238ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (377.819975ms) +✔ N10: fake conformance (33.25503ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (448.893911ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (274.50042ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (282.203398ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (357.297375ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (553.073748ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (399.560942ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (202.898286ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (373.436654ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (738.046405ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (548.215195ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (435.339925ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (250.654651ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (205.315195ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (202.925523ms) +✔ 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 (221.938844ms) +✔ 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.435211ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 58691.396965 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1060:1 +✖ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (702.582403ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [ + + [ + + 1442985, + + 'SIGKILL' + + ] + + ] + - [] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51a/wt2/packages/conversation/tests/cohort.test.mjs:1077:10) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: [ [ 1442985, 'SIGKILL' ] ], + expected: [], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r1/mut/closekill-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/closekill-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/closekill-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/closeproof-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/closeproof-conversation.txt new file mode 100644 index 00000000..b9f815c6 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/closeproof-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (205.550895ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.410503ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.181897ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (344.835917ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (396.592231ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (246.193583ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (194.469013ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (173.336252ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.581695ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11992.293941ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (40857.994621ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (264.780569ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (148.519791ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (667.189746ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (278.746176ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (336.068035ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (105.245664ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (191.549809ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (275.804022ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (748.850029ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (180.790939ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (88.155819ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (71.452822ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.446252ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.103606ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.797249ms) +✔ no shim from this file's tests is left running for the after hook (24.112953ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (932.935711ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (454.108096ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (767.879175ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3032.64863ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (744.765619ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (843.209565ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (745.855439ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5950.405787ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (940.55669ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (909.862952ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (727.097715ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (766.654356ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (305.279168ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (494.898981ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3261.372773ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (45.789367ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (313.533265ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (340.352453ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (80.889874ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (645.596324ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (703.340151ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1584.659033ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (895.935357ms) +✔ 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) (447.274728ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (332.748041ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2849.645647ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (551.78417ms) +✔ R9: a release that ended unavailable is tried again on recover (646.214614ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (437.324226ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (679.229188ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (281.656088ms) +✔ no shim from this file's tests is left running for the after hook (28.087871ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (215.765155ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (198.804426ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (183.99211ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (285.129339ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (199.948056ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.416311ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (197.600519ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (296.344056ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (194.39717ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (196.775983ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (209.737985ms) +✔ 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.965713ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.109949ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (205.575223ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (224.257798ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (248.783911ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (192.978172ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.434109ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.276383ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.31502ms) +✔ 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.515926ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.926352ms) +✔ 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.466031ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.698725ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.35861ms) +✔ 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.330241ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.102453ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (221.733433ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (249.931776ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (285.254264ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (802.515581ms) +✔ H4: self-takeover is refused (179.057546ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (418.968613ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1024.113402ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (212.632053ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (538.606432ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (260.373876ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (123.797207ms) +✔ H13: a retry with the same request ID and different text is refused (180.827013ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (606.292237ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (394.854099ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (240.59771ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (888.530337ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (371.64517ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (796.670263ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (203.74184ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (311.138205ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.709113ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (708.691447ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (587.215564ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (673.646442ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3193.2873ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (640.866616ms) +✔ no shim from this file's tests is left running for the after hook (25.990162ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.128885ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.481432ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.623429ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.217766ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.33977ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.795822ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.760672ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.095544ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.293901ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.748801ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (3.984032ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.392238ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (8.500068ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (11.719328ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.802761ms) +✔ F9: registrations never add or redirect a root (1.816901ms) +✔ F10: a header cwd naming another project is refused (4.364675ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.73025ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.336075ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.096877ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.432046ms) +✔ F13: compaction is a marker in place, then the retained content (0.80075ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (502.552237ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.089468ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.52742ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.399334ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.120919ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.042442ms) +✔ every page and cursor is a valid CHAT-01 record (754.325291ms) +✔ the engine pin holds for the installed package (1.583443ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (265.281127ms) +✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (512.720281ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (234.59464ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (481.480299ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (433.705274ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (232.782843ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.867756ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (342.340682ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (182.640112ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (289.334667ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (472.19951ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1887.822487ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (182.991451ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (293.684695ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (200.862433ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (344.112714ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (420.048733ms) +✔ N10: fake conformance (31.833497ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (463.150611ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (292.445431ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (293.634575ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (387.772434ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (379.824999ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (338.193134ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (128.522304ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (402.824138ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (786.665034ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (468.874339ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (489.996411ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (251.477422ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (203.929459ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (170.207214ms) +✔ 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 (170.09043ms) +✔ 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 (211.745063ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60941.365826 diff --git a/agents/darkwing/work/queue-51-review/r1/mut/closeproof-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/closeproof-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/closeproof-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/manifest-after.txt b/agents/darkwing/work/queue-51-review/r1/mut/manifest-after.txt new file mode 100644 index 00000000..7adc1aca --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/manifest-after.txt @@ -0,0 +1,5 @@ +agents/dewey/work/queue-51/evidence.md: OK +packages/conversation/README.md: OK +packages/conversation/src/controller.mjs: OK +packages/conversation/tests/cohort.test.mjs: OK +packages/conversation/tests/ctrl-child.mjs: OK diff --git a/agents/darkwing/work/queue-51-review/r1/mut/mutate.py b/agents/darkwing/work/queue-51-review/r1/mut/mutate.py new file mode 100644 index 00000000..353353ab --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/mutate.py @@ -0,0 +1,37 @@ +# mutate.py : apply one named mutant (each edit exact text, must match once). Run from the tree root. +import sys +CT = "packages/conversation/src/controller.mjs" +PROOF = " if (b.state !== \"stopped\" || b.admission !== \"closed\" || !this.#stopped(s)) return refused(\"stop-proof\");\n" +RECREL = " await this.#releaseListed(\"recover\", this.claim);\n" +CONF = " if (!this.#checkConfirmation(r, c, \"recover\")) return refused(\"confirmation\");\n" +KILL = " const kill = killEngine && this.b?.state !== \"stopped\";\n" +M = { + # Dewey's, spot-checked. + "norecover": [(CT, PROOF + RECREL, PROOF)], + "nostartfree": [(CT, " for (const h of [prior, seatHead]) {\n", " for (const h of []) {\n")], + "nostartstopped": [(CT, " await this.#releaseListed(\"start\", { claimId: classified.claimId, record: classified.record });\n", "")], + "closekill": [(CT, KILL, " const kill = killEngine;\n")], + "nopush": [(CT, " this.#push({ kind: \"evidence\", evidence: entry });\n", "")], + # The recover release moves after the confirmation check (the packet's K6 reading). + "recafter": [(CT, PROOF + RECREL, PROOF), (CT, CONF, CONF + RECREL)], + # The recover release moves before the stop-proof check. + "recbefore": [(CT, PROOF + RECREL, RECREL + PROOF)], + # The free path checks only the session head. + "noseat": [(CT, " for (const h of [prior, seatHead]) {\n", " for (const h of [prior]) {\n")], + # The free path drops the same-claim skip for the seat head. + "nodedupe": [(CT, " || (h === seatHead && prior.n > 0 && !prior.damaged && h.record.claimId === prior.record.claimId)) continue;", ") continue;")], + # #releaseListed sends a request even when the unit reads absent. + "nolookup": [(CT, " if ((await this.units.lookup(claim.record)).state === \"absent\") return null;\n", "")], + # #releaseListed never drops an earlier result, so #releaseScope returns it. + "nodelete": [(CT, " if (this.scopeReleases.get(claim.claimId) === earlier) this.scopeReleases.delete(claim.claimId);\n", "")], + # close skips the signal only while #stopped() still accepts the stop. + "closeproof": [(CT, KILL, " const kill = killEngine && !(this.b?.state === \"stopped\" && this.#stopped(this.stops.get(this.b.stop)));\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-51-review/r1/mut/nodedupe-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/nodedupe-conversation.txt new file mode 100644 index 00000000..8e000d53 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nodedupe-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (196.834037ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (28.041154ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.923674ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (342.544722ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (391.024486ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (233.423918ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (186.578754ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (187.679481ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.537777ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12307.53474ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39667.09039ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (248.570783ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (144.045202ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (612.973453ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (272.113438ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (368.500005ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (109.795042ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (201.290988ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (272.19682ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (747.379767ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (186.015361ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (89.503146ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (76.047744ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.127432ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.098707ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.835905ms) +✔ no shim from this file's tests is left running for the after hook (26.267428ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (932.633931ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (434.435489ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (746.124206ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3063.533325ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (745.98246ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (887.291282ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (819.944609ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5696.007721ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (924.561309ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (628.871706ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (639.389129ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (670.083858ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (322.334351ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (531.380151ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3303.006865ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (19.584546ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (292.331343ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (365.718909ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.743056ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (572.590835ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (766.122154ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1231.462543ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (657.796982ms) +✔ 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.962957ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (348.465371ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2990.906226ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (502.715842ms) +✔ R9: a release that ended unavailable is tried again on recover (493.612833ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (644.452999ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (411.181423ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (522.652115ms) +✔ no shim from this file's tests is left running for the after hook (24.21293ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (197.544555ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (180.927161ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (188.685089ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (262.467466ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (198.17823ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.425204ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (184.765492ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (289.101415ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (203.908011ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (169.336186ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (202.635375ms) +✔ 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 (205.763413ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.992133ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (196.271186ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (253.283145ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (361.497793ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (290.876449ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.453345ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.265643ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.345935ms) +✔ 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.489515ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.01744ms) +✔ 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.520451ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.729927ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.372457ms) +✔ 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.340189ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.100259ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (218.051446ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (238.631679ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (272.890022ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (779.657779ms) +✔ H4: self-takeover is refused (178.916889ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (403.957952ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1136.062111ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (366.274294ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (702.062504ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (355.897623ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (210.808243ms) +✔ H13: a retry with the same request ID and different text is refused (196.296527ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (637.527656ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (275.630012ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (206.551891ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1084.236601ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (427.390357ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (856.560721ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (210.108222ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (351.713192ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (1.236255ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (670.499736ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (547.46762ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (808.888263ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2842.495609ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (704.723598ms) +✔ no shim from this file's tests is left running for the after hook (33.258711ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.975367ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.696862ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.733772ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.401849ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.398774ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.821715ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.788783ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.130938ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.055484ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.321302ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.349134ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.255647ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.059474ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (11.124459ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.48059ms) +✔ F9: registrations never add or redirect a root (1.665799ms) +✔ F10: a header cwd naming another project is refused (3.850429ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.731778ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.15809ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.798402ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.252442ms) +✔ F13: compaction is a marker in place, then the retained content (0.96342ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (495.475644ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.920639ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.552558ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.473528ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.221376ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.156845ms) +✔ every page and cursor is a valid CHAT-01 record (753.515262ms) +✔ the engine pin holds for the installed package (1.687629ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (250.067891ms) +✔ 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) (533.104095ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (239.678649ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (451.622405ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (400.645554ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (226.962554ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.79831ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (327.62388ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (189.262142ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (287.662028ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (466.622881ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1880.845257ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (160.849052ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (300.387676ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (212.94235ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (242.250617ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (418.531596ms) +✔ N10: fake conformance (32.997077ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (408.459664ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (283.087944ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (394.573631ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (489.565257ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (440.980906ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (416.897352ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (204.589211ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (380.991979ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (706.854768ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (440.991358ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (366.861771ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (236.242181ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (150.314056ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (163.99469ms) +✔ 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.86712ms) +✔ 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 (160.006271ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60116.530405 diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nodedupe-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/nodedupe-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nodedupe-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nodelete-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/nodelete-conversation.txt new file mode 100644 index 00000000..a6f244d8 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nodelete-conversation.txt @@ -0,0 +1,217 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (236.8361ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (39.048335ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.440216ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (371.763996ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (488.931064ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (269.42197ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (227.203131ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (224.781308ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.645048ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12441.039184ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (37411.215309ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (264.421541ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (144.687351ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (660.383067ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (286.18624ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (350.816143ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (127.142083ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (191.661412ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (242.722064ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (733.086112ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (181.055117ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (88.686978ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (79.492262ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.438555ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.08855ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.914283ms) +✔ no shim from this file's tests is left running for the after hook (25.46724ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (652.623886ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (617.566284ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (632.37154ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2823.328719ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (631.952554ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (640.470365ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (569.821836ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5654.690902ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1019.166939ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (729.823397ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (535.291192ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (733.457215ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (426.900048ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (570.261814ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3307.688979ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (23.276718ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (465.048432ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (370.429377ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.267045ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (499.797318ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (604.210174ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1263.714179ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (729.078183ms) +✔ 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) (455.251479ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (152.213198ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2929.012905ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (569.979029ms) +✖ R9: a release that ended unavailable is tried again on recover (556.838892ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (524.307452ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (455.204206ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (329.912ms) +✔ no shim from this file's tests is left running for the after hook (28.258848ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (240.901876ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (220.489443ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (206.420534ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (426.353541ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (207.770506ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.510097ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (222.036221ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (329.906421ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (224.646094ms) +✔ 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.756566ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (220.516306ms) +✔ 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 (224.14534ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.974834ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (242.265805ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (280.711214ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (282.991692ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (221.365148ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.419058ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.272069ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.318086ms) +✔ 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.483654ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.017034ms) +✔ 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.482621ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.723086ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.360495ms) +✔ 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.317702ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.123741ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (226.010206ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (286.255895ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (313.162967ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1003.315549ms) +✔ H4: self-takeover is refused (223.685184ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (462.503102ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1215.774926ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (250.407195ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (801.593452ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (463.081901ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (238.89065ms) +✔ H13: a retry with the same request ID and different text is refused (215.060208ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (651.816555ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (319.071837ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (236.44943ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (955.455604ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (458.664259ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (832.807221ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (214.416406ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (484.751643ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.633021ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (817.989005ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (629.579346ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (747.117691ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3093.498574ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (681.166884ms) +✔ no shim from this file's tests is left running for the after hook (26.162721ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (9.036479ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.905005ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.9364ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.599209ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.434377ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.943999ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.852303ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.275232ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.309238ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.781915ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.945559ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (6.881891ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (9.178528ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.928282ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.931254ms) +✔ F9: registrations never add or redirect a root (1.843434ms) +✔ F10: a header cwd naming another project is refused (4.506258ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.735231ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.417528ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.084037ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.787682ms) +✔ F13: compaction is a marker in place, then the retained content (0.784805ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (535.331931ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.688916ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.562006ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.475918ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.204514ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.041276ms) +✔ every page and cursor is a valid CHAT-01 record (806.31104ms) +✔ the engine pin holds for the installed package (1.889331ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (283.680427ms) +✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (540.189565ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (246.146336ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (540.29925ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (585.325572ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (256.102455ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.184113ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (395.54008ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (223.137924ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (284.441604ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (515.750154ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2039.654224ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (285.717853ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (323.39379ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (206.042253ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (259.087998ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (463.454306ms) +✔ N10: fake conformance (32.744027ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (500.521714ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (300.380635ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (306.885643ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (404.836047ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (449.70653ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (408.963362ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (200.411069ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (513.401722ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (1081.24731ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (658.608538ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (581.946152ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (259.442011ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (232.713398ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (225.511383ms) +✔ 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 (275.167078ms) +✔ 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 (261.397379ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 58270.522503 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:998:1 +✖ R9: a release that ended unavailable is tried again on recover (556.838892ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + [ + [ + 'force-stop', + 'unavailable', + null + ], + - [ + - 'recover', + - 'released', + - 'absent' + - ] + ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51a/wt2/packages/conversation/tests/cohort.test.mjs:1015: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: [ [ 'force-stop', 'unavailable', null ] ], + expected: [ [ 'force-stop', 'unavailable', null ], [ 'recover', 'released', 'absent' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nodelete-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/nodelete-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nodelete-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nolookup-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/nolookup-conversation.txt new file mode 100644 index 00000000..76952d96 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nolookup-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (224.749369ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (35.995944ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.815514ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (342.644956ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (365.049033ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (250.552685ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (192.675824ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (195.368999ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.582261ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11535.748215ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (40105.301509ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (371.129039ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (209.050744ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (623.779365ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (373.585079ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (452.471974ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (197.346511ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (287.515079ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (549.352569ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1527.876854ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (384.8411ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (122.157348ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (88.80827ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.163652ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.096472ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.757437ms) +✔ no shim from this file's tests is left running for the after hook (25.501085ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (884.854773ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (451.411438ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (762.068338ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3021.352715ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (658.804855ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (766.971263ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (708.834299ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5783.824287ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (945.285148ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (911.379655ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (653.781103ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (681.670732ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (326.655145ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (523.234068ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3294.302399ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (29.532746ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (321.096858ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (391.585769ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.418742ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (454.806936ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (1008.703162ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1228.482703ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (655.910183ms) +✔ 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) (461.548272ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (124.446381ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2865.784836ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (552.744265ms) +✔ R9: a release that ended unavailable is tried again on recover (800.464282ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (474.239603ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (674.622045ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (562.800973ms) +✔ no shim from this file's tests is left running for the after hook (23.284383ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (206.048725ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (200.62156ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (183.76066ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (249.769411ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (186.839552ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.463391ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (186.202877ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (297.012988ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (208.878418ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (195.387714ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (203.917716ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (209.127806ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.099616ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (205.235259ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (219.929376ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (259.277378ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (201.812771ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.395376ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.290022ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.314526ms) +✔ 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.502137ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.815403ms) +✔ 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.49359ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.691218ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.341433ms) +✔ 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.319261ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.098933ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (219.138506ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (250.022166ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (284.804286ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (756.200767ms) +✔ H4: self-takeover is refused (195.247185ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (435.915361ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1020.840613ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (222.691119ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (522.73174ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (254.532568ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (113.44448ms) +✔ H13: a retry with the same request ID and different text is refused (177.813443ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (578.425632ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (288.021608ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (195.231508ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (836.279981ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (328.848778ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (682.897361ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (193.028781ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (332.568801ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.637228ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (667.512272ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (558.10806ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (700.589366ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3074.732596ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (585.995976ms) +✔ no shim from this file's tests is left running for the after hook (25.940147ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.852844ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.599646ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.7318ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.256662ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.404358ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.797442ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.779771ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.125134ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (2.980777ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.784151ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.256738ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (6.277598ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.443562ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.87434ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.887229ms) +✔ F9: registrations never add or redirect a root (1.755602ms) +✔ F10: a header cwd naming another project is refused (4.144537ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.810194ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.59397ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (3.252891ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (2.151532ms) +✔ F13: compaction is a marker in place, then the retained content (0.857797ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (508.793862ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.679026ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.478259ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.264763ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.104561ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.952253ms) +✔ every page and cursor is a valid CHAT-01 record (807.557635ms) +✔ the engine pin holds for the installed package (1.711485ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (272.100076ms) +✔ 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) (516.656065ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (254.916874ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (467.401815ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (387.070817ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (226.316731ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.000126ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (332.552377ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (198.57173ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (294.096346ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (474.718507ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1875.611714ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (189.067785ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (282.520412ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (150.338218ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (211.239893ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (362.094061ms) +✔ N10: fake conformance (30.512498ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (422.793097ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (270.878119ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (280.468863ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (318.839443ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (334.331987ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (346.927803ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (187.068838ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (307.579965ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (665.295139ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (411.321881ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (448.337937ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (159.699289ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (152.676534ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (167.618716ms) +✔ 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 (213.958198ms) +✔ 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.516245ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 62115.895844 diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nolookup-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/nolookup-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nolookup-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nopush-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/nopush-conversation.txt new file mode 100644 index 00000000..f279bfc4 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nopush-conversation.txt @@ -0,0 +1,195 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (257.939928ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (31.496913ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.569591ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (341.588216ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (437.239974ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (235.174368ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (192.999307ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (191.909723ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.539526ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12102.344055ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39892.261255ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (254.785021ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (145.225442ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (648.931661ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (294.178768ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (433.670488ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (126.486086ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (242.58471ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (273.525596ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (746.100033ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (186.732067ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (100.662461ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (91.239049ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.31016ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.248128ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.767472ms) +✔ no shim from this file's tests is left running for the after hook (25.338445ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1010.843067ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (447.631215ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (820.16126ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2997.567301ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (718.358591ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (843.420293ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (764.514109ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5496.369869ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (942.021874ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (823.678828ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (620.6415ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (669.406889ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (341.763234ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (431.021375ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3316.885569ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (26.531239ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (313.984005ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (266.29914ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.771994ms) +✖ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (4573.65396ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (525.815088ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1317.496121ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (605.24788ms) +✔ 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) (653.065177ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (145.3808ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2614.787925ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (630.686816ms) +✔ R9: a release that ended unavailable is tried again on recover (669.384875ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (410.65655ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (373.935555ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (281.924421ms) +✔ no shim from this file's tests is left running for the after hook (25.203419ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (245.240716ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (190.306951ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (185.951309ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (325.521264ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (193.164612ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.463246ms) +✔ 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.909936ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (297.171694ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (202.206241ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (209.313729ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (214.023115ms) +✔ 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 (205.385621ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.969967ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (219.793123ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (207.928668ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (239.611539ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (194.605174ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.40381ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.278134ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.304441ms) +✔ 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.52236ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.019967ms) +✔ 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.442338ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.705893ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.340672ms) +✔ 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.330622ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.098373ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (245.651372ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (240.216486ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (329.934627ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (787.454926ms) +✔ H4: self-takeover is refused (189.570154ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (449.178522ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1027.240555ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (145.448035ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (565.964516ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (274.244313ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (195.611117ms) +✔ H13: a retry with the same request ID and different text is refused (192.840402ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (555.118918ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (431.875451ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (195.218042ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (903.74744ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (339.984293ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (780.711901ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (206.240523ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (350.014301ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.705896ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (667.643367ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (594.546245ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (816.611211ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3176.056149ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (659.756902ms) +✔ no shim from this file's tests is left running for the after hook (26.386824ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.90814ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.603662ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.593617ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.279957ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.489417ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.096095ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (1.17701ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.505473ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.880301ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.603018ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.620745ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.798982ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.083254ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.32376ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.900526ms) +✔ F9: registrations never add or redirect a root (1.928152ms) +✔ F10: a header cwd naming another project is refused (3.86009ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.728601ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.195696ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.104276ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.840874ms) +✔ F13: compaction is a marker in place, then the retained content (1.276089ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (504.116892ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.281353ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.372543ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.380037ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (0.995985ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.844079ms) +✔ every page and cursor is a valid CHAT-01 record (756.229736ms) +✔ the engine pin holds for the installed package (2.648388ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (269.498531ms) +✔ 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) (504.995949ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (253.59072ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (511.162486ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (455.956434ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (223.284724ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.760768ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (343.994298ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (193.516411ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (307.845692ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (476.28228ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1857.185709ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (196.076262ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (280.058242ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (179.659134ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (388.839127ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (431.49362ms) +✔ N10: fake conformance (31.742482ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (437.30471ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (291.385822ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (302.819404ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (366.758978ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (370.498437ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (360.535537ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (211.403961ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (280.606462ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (687.798222ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (586.047751ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (600.012114ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (205.467593ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (204.694887ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (176.068027ms) +✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (215.324131ms) +✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (217.014683ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 61376.021464 + +✖ 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 (4573.65396ms) + Error: timed out waiting for the pushed release + at until (file:///home/jwoltje/darkwing-scratch/r51a/wt2/packages/conversation/tests/cohort.test.mjs:49:33) + at async TestContext. (file:///home/jwoltje/darkwing-scratch/r51a/wt2/packages/conversation/tests/cohort.test.mjs:777: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-51-review/r1/mut/nopush-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/nopush-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nopush-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/norecover-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/norecover-conversation.txt new file mode 100644 index 00000000..0861ce88 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/norecover-conversation.txt @@ -0,0 +1,217 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (246.99436ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (30.901532ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (20.869022ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (464.165561ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (627.781327ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (280.240148ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (399.952311ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (241.434987ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.622379ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12420.453592ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (40163.959466ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (261.527996ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (139.498441ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (623.174328ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (309.576028ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (372.123439ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (129.784804ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (211.446506ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (276.340158ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (761.896244ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (193.17796ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (98.250569ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (75.087095ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.689072ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.140326ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.822711ms) +✔ no shim from this file's tests is left running for the after hook (25.169172ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1153.644477ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (675.226253ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (818.608803ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3052.494294ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (802.129176ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (818.667765ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (714.445234ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5503.039017ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1315.466059ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (952.723453ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (663.739046ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (693.50565ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (331.903998ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (526.494209ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3221.113575ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (21.524739ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (358.242866ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (337.81763ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.892149ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (644.411993ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (808.862637ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1358.919361ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (733.965483ms) +✔ 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) (651.46281ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (154.037506ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2676.988287ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (438.728694ms) +✖ R9: a release that ended unavailable is tried again on recover (556.368763ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (545.229541ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (395.729401ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (521.626267ms) +✔ no shim from this file's tests is left running for the after hook (25.107773ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (241.010826ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (285.421213ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (225.431685ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (480.877076ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (309.941015ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.44028ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (313.352464ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (415.980679ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (236.850466ms) +✔ 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.971297ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (207.222406ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (204.881988ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (9.991236ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (212.173099ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (230.043292ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (239.774517ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (167.832642ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.399434ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.26081ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.331555ms) +✔ 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.484341ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.096713ms) +✔ 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.58483ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.929015ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.447753ms) +✔ 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.348761ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.106423ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (232.721209ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (342.570885ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (453.10619ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1195.147172ms) +✔ H4: self-takeover is refused (219.942246ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (438.439913ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (990.792711ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (162.373226ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (508.485619ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (258.699051ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (135.378128ms) +✔ H13: a retry with the same request ID and different text is refused (187.976356ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (552.2794ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (295.140382ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (203.613969ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (917.502204ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (301.486448ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (744.05207ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (193.235195ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (321.973588ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.686565ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (665.46743ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (574.559242ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (1057.46203ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3290.245801ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (840.649716ms) +✔ no shim from this file's tests is left running for the after hook (25.355045ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.239139ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.701374ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.742433ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.364966ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.484507ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.147355ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.796707ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.621794ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.121015ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.980574ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.257532ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.640014ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.807695ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.836012ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.109745ms) +✔ F9: registrations never add or redirect a root (1.876928ms) +✔ F10: a header cwd naming another project is refused (4.324282ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.788949ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.596055ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.381506ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.605943ms) +✔ F13: compaction is a marker in place, then the retained content (0.846609ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (505.367572ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.359485ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.375117ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.159127ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.03758ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.984987ms) +✔ every page and cursor is a valid CHAT-01 record (758.445112ms) +✔ the engine pin holds for the installed package (1.864989ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (279.158668ms) +✔ 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) (515.628694ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (251.396848ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (614.478279ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (669.218856ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (335.582187ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.651897ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (588.514662ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (228.708473ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (298.418395ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (470.567588ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1886.290908ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (190.512709ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (282.03773ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (137.162694ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (229.439998ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (389.538267ms) +✔ N10: fake conformance (30.869646ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (504.961871ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (251.830008ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (278.163017ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (347.39649ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (360.206114ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (347.882003ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (191.713541ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (333.180199ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (652.612411ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (708.818141ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (684.104036ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (232.530772ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (224.047231ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (213.713756ms) +✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (232.207413ms) +✔ 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 (174.313556ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 61595.315009 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:998:1 +✖ R9: a release that ended unavailable is tried again on recover (556.368763ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + [ + [ + 'force-stop', + 'unavailable', + null + ], + - [ + - 'recover', + - 'released', + - 'absent' + - ] + ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51a/wt2/packages/conversation/tests/cohort.test.mjs:1015: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: [ [ 'force-stop', 'unavailable', null ] ], + expected: [ [ 'force-stop', 'unavailable', null ], [ 'recover', 'released', 'absent' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r1/mut/norecover-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/norecover-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/norecover-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/noseat-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/noseat-conversation.txt new file mode 100644 index 00000000..28579754 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/noseat-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (202.528302ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (30.457307ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.073875ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (361.231718ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (397.208392ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (249.253238ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (202.438159ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (200.217161ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.602026ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12456.449907ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39765.519477ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (273.123143ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (163.010444ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (639.971322ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (289.81115ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (345.826753ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (107.972008ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (199.121028ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (257.470947ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (746.830845ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (188.536538ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (93.663981ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (72.227117ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.115392ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.062233ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.718306ms) +✔ no shim from this file's tests is left running for the after hook (24.846035ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (948.534115ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (468.605566ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (779.168981ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3498.220453ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (839.34526ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (896.474556ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (766.37087ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5597.898916ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (982.852267ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (854.802012ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (632.290606ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (631.070643ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (302.463079ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (471.722775ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3210.94592ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (23.93474ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (345.998598ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (284.952108ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (84.445491ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (588.203575ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (729.529781ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1128.091374ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (677.274877ms) +✔ 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) (481.707627ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (154.169895ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2918.627853ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (534.70726ms) +✔ R9: a release that ended unavailable is tried again on recover (700.53753ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (443.146583ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (460.424339ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (292.58824ms) +✔ no shim from this file's tests is left running for the after hook (25.034465ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (195.89819ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (196.100689ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (195.672732ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (280.733087ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (195.8338ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.426724ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (190.622713ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (302.822007ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (211.513708ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (201.389824ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (195.104158ms) +✔ 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 (412.121283ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.045215ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (393.997074ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (401.58399ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (378.070477ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (230.324841ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.41093ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.25723ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.311441ms) +✔ 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.520216ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.33686ms) +✔ 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.492657ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.737691ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.38442ms) +✔ 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.340065ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.11179ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (221.004191ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (253.004244ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (286.840051ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (803.256811ms) +✔ H4: self-takeover is refused (193.779514ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (522.775915ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1719.667248ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (158.810762ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (645.323356ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (353.468326ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (164.928457ms) +✔ H13: a retry with the same request ID and different text is refused (193.686819ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (726.520874ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (441.481802ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (260.233334ms) +✔ 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.055601ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (364.530534ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (795.672446ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (192.703717ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (319.660486ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.809652ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (615.008021ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (529.750048ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (716.04563ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2990.453574ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (634.132823ms) +✔ no shim from this file's tests is left running for the after hook (24.077903ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.934947ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.578196ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.671979ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.343139ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.373371ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.806137ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.870781ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.270731ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.026936ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.328001ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.555185ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.804421ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.937292ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.302225ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.999062ms) +✔ F9: registrations never add or redirect a root (1.765407ms) +✔ F10: a header cwd naming another project is refused (4.013599ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.766872ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.234638ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.015528ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.444119ms) +✔ F13: compaction is a marker in place, then the retained content (0.782915ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (530.065863ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.736529ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.636648ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.558861ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.174013ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.412067ms) +✔ every page and cursor is a valid CHAT-01 record (758.282973ms) +✔ the engine pin holds for the installed package (1.864722ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (277.781254ms) +✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (552.124652ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (235.47957ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (476.510763ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (423.079824ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (234.474453ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.928105ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (351.676263ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (200.809854ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (300.917359ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (847.301474ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2032.923722ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (196.4855ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (246.84767ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (194.030136ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (275.8032ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (480.302013ms) +✔ N10: fake conformance (32.759305ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (666.549633ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (328.29795ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (297.989067ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (379.165818ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (368.878252ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (384.058393ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (215.185469ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (366.767895ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (587.574961ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (466.881862ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (390.801016ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (205.017552ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (135.994343ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (158.715681ms) +✔ 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 (166.832448ms) +✔ 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 (182.94382ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60445.477453 diff --git a/agents/darkwing/work/queue-51-review/r1/mut/noseat-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/noseat-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/noseat-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nostartfree-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/nostartfree-conversation.txt new file mode 100644 index 00000000..f579584e --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nostartfree-conversation.txt @@ -0,0 +1,215 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (234.98765ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (36.919617ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (21.137455ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (388.205831ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (398.368849ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (242.234698ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (204.481945ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (209.219718ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.908381ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12627.540245ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38642.468607ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (409.454104ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (233.480426ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (903.186872ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (337.59524ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (381.708203ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (115.673619ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (208.682814ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (298.191442ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (862.154963ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (262.355726ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (95.806632ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (76.345342ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.214191ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.084572ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.786162ms) +✔ no shim from this file's tests is left running for the after hook (25.121134ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (972.623276ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (471.225888ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (761.400162ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3066.340404ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (770.091014ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (781.732564ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (741.520928ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5742.607797ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1057.91815ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (1373.323484ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (727.368668ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (752.89923ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (345.980089ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (475.024312ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3253.103305ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (22.455908ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (333.661996ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (316.888538ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.334479ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (594.798892ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (737.105684ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1179.967047ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (659.008116ms) +✔ 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) (466.693903ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (140.274343ms) +✖ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2786.322186ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (614.220264ms) +✔ R9: a release that ended unavailable is tried again on recover (690.553874ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (754.43752ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (448.805208ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (449.076172ms) +✔ no shim from this file's tests is left running for the after hook (23.711545ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (224.253029ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (221.987412ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (208.66499ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (292.863582ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (200.512735ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.436449ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (206.159464ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (301.883074ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (215.748045ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (194.580937ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (209.018833ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (220.315706ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.909016ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (230.25134ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (243.624964ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (260.590858ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (208.115948ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.415812ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.263656ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.315789ms) +✔ 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.487124ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.21861ms) +✔ 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.465854ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.828601ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.499949ms) +✔ 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.371088ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.120075ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (225.87963ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (267.604545ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (304.181102ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (838.790212ms) +✔ H4: self-takeover is refused (195.624638ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (443.975261ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1134.024499ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (241.107502ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (693.359645ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (280.267366ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (216.489353ms) +✔ H13: a retry with the same request ID and different text is refused (211.523694ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (593.572694ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (312.566034ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (172.180583ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (890.463045ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (378.518385ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (807.215217ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (217.676435ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (353.933776ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.637143ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (749.44956ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (574.956873ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (971.881789ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2899.400607ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (661.802522ms) +✔ no shim from this file's tests is left running for the after hook (27.482678ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.765149ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.497229ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.574453ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.113398ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.26398ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.681336ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.727207ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.135811ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (6.018319ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.838253ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (3.980552ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.159075ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.054489ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.193234ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.6047ms) +✔ F9: registrations never add or redirect a root (1.869116ms) +✔ F10: a header cwd naming another project is refused (4.319405ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.689621ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.302148ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.866619ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.253219ms) +✔ F13: compaction is a marker in place, then the retained content (0.707427ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (541.093403ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.732076ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.638218ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.306675ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.044926ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.11109ms) +✔ every page and cursor is a valid CHAT-01 record (776.812474ms) +✔ the engine pin holds for the installed package (1.584778ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (282.748556ms) +✔ 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) (564.082177ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (255.769073ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (517.832872ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (450.802458ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (237.813752ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.957248ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (360.135084ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (209.43028ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (295.952634ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (497.566803ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1901.643865ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (216.01891ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (301.047363ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (219.184588ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (225.84592ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (410.401265ms) +✔ N10: fake conformance (32.675719ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (426.973832ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (296.941107ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (297.735413ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (373.781181ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (387.541263ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (370.846505ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (218.362227ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (410.059754ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (728.931887ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (521.345116ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (497.979233ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (231.614432ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (131.626354ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (129.87504ms) +✔ 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 (251.757589ms) +✔ 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 (219.422773ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 61558.998081 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:965:3 +✖ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2786.322186ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [] + - [ + - [ + - 'start', + - 'released', + - 'absent', + - 'mosaic-chat-cbd40fb56e39014705558d330', + - 'cbd40fb56e39014705558d330' + - ] + - ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51a/wt2/packages/conversation/tests/cohort.test.mjs:987: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: [ [ 'start', 'released', 'absent', 'mosaic-chat-cbd40fb56e39014705558d330', 'cbd40fb56e39014705558d330' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nostartfree-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/nostartfree-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nostartfree-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nostartstopped-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/nostartstopped-conversation.txt new file mode 100644 index 00000000..a7e81846 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nostartstopped-conversation.txt @@ -0,0 +1,215 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (212.254808ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (39.768313ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.884864ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (377.387155ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (401.341018ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (241.007876ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (208.181818ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (188.175713ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.397683ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12263.466678ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (37504.630415ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (266.489717ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (154.183006ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (685.593038ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (288.402975ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (432.685618ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (154.356712ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (409.206736ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (402.452245ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1022.840846ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (266.702599ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (134.298438ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (79.186573ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.501544ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.081532ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.812411ms) +✔ no shim from this file's tests is left running for the after hook (25.429378ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (764.784052ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (477.440705ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (712.262171ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3305.647594ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (1136.762746ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (643.42804ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (532.230179ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5267.078207ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (823.678741ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (681.70869ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (498.701653ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (700.977833ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (327.007168ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (494.988519ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3282.341856ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (45.268036ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (414.908703ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (331.379521ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (61.684588ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (470.956306ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (724.368124ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1193.861319ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (909.172989ms) +✔ 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.841809ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (134.885137ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (3175.793086ms) +✖ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (602.128806ms) +✔ R9: a release that ended unavailable is tried again on recover (635.752864ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (606.272804ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (425.541447ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (500.78146ms) +✔ no shim from this file's tests is left running for the after hook (23.82576ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (231.510844ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (214.789378ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (218.567023ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (295.691234ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (213.030263ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.431582ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (209.391636ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (314.778654ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (212.026297ms) +✔ 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.897473ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (470.918725ms) +✔ 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 (387.268809ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.300394ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (311.325403ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (264.288096ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (247.166756ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (194.129601ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.44017ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.282082ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.344459ms) +✔ 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.49483ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.199814ms) +✔ 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.504914ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.813156ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.475684ms) +✔ 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.350984ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.102422ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (227.949003ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (275.73004ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (306.38264ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (843.908345ms) +✔ H4: self-takeover is refused (200.1921ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (443.72578ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1656.316181ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (222.482514ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (549.202345ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (256.233476ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (162.399556ms) +✔ H13: a retry with the same request ID and different text is refused (234.118251ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (1314.025113ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (300.167586ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (211.640663ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (924.800778ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (362.131619ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (708.780567ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (188.11981ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (333.749656ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.661585ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (664.483958ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (579.480068ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (661.61539ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3039.374404ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (668.341665ms) +✔ no shim from this file's tests is left running for the after hook (25.985533ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (9.021322ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.624924ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.93008ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.42202ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.506537ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.778166ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.743172ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.15252ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.333701ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.841504ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.115532ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.043931ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.21641ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.866679ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.754744ms) +✔ F9: registrations never add or redirect a root (1.570956ms) +✔ F10: a header cwd naming another project is refused (4.198777ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.687105ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.935138ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.948324ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.442324ms) +✔ F13: compaction is a marker in place, then the retained content (0.806558ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (500.700335ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.511011ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.485825ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.234296ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.040994ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.892249ms) +✔ every page and cursor is a valid CHAT-01 record (732.235874ms) +✔ the engine pin holds for the installed package (1.712694ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (263.457975ms) +✔ 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) (521.872098ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (235.448652ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (515.426054ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (463.599087ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (248.663692ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.821616ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (373.338131ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (186.510561ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (297.70751ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (956.487037ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1965.32879ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (274.685101ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (818.414257ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (277.072064ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (221.448169ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (411.972521ms) +✔ N10: fake conformance (34.158934ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (461.438078ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (289.138894ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (317.356963ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (355.342573ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (329.243163ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (334.692329ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (195.313937ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (334.781267ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (711.834917ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (528.218885ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (439.15706ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (234.990479ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (190.791812ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (206.415108ms) +✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (219.663832ms) +✔ 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 (219.464205ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 58927.769917 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:965:3 +✖ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (602.128806ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [] + - [ + - [ + - 'start', + - 'released', + - 'absent', + - 'mosaic-chat-cc1d047633b16c356a0b014d1', + - 'cc1d047633b16c356a0b014d1' + - ] + - ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51a/wt2/packages/conversation/tests/cohort.test.mjs:987: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: [ [ 'start', 'released', 'absent', 'mosaic-chat-cc1d047633b16c356a0b014d1', 'cc1d047633b16c356a0b014d1' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r1/mut/nostartstopped-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/nostartstopped-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/nostartstopped-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/recafter-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/recafter-conversation.txt new file mode 100644 index 00000000..8ad58abe --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/recafter-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (249.98635ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (36.710313ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (24.488116ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (410.721364ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (481.76731ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (279.349201ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (201.124461ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (201.486159ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (3.802768ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12741.251702ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39625.057867ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (267.120165ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (144.078287ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (621.648736ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (272.562943ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (344.107258ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (107.848114ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (192.456146ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (264.864906ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (740.41237ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (185.373014ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (93.212276ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (78.095926ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.133325ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.086519ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.733216ms) +✔ no shim from this file's tests is left running for the after hook (24.414856ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1000.16422ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (489.224425ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (828.703433ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3070.328024ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (789.557996ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (908.353698ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (951.397472ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5625.491243ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1046.263732ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (859.054305ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (703.191687ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (615.541408ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (307.858326ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (492.14178ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3202.798878ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (21.944242ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (309.578987ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (239.899358ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (92.485749ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (540.543009ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (676.245988ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1125.953887ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (641.967941ms) +✔ 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.719581ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (132.988762ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2842.04892ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (547.763179ms) +✔ R9: a release that ended unavailable is tried again on recover (619.479368ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (415.093409ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (598.428295ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (276.095117ms) +✔ no shim from this file's tests is left running for the after hook (24.449959ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (211.214744ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (210.133407ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (234.545823ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (275.172979ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (200.585871ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.783262ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (190.087619ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (297.248287ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (210.171893ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (197.687518ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (209.074217ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (204.760733ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.432291ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (204.685456ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (212.393556ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (231.650284ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (193.007267ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.664362ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.399194ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.568341ms) +✔ 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.688641ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.557812ms) +✔ 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.680586ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.310275ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.76335ms) +✔ 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.68179ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.184998ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (305.016608ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (294.51112ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (295.541837ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (800.961433ms) +✔ H4: self-takeover is refused (198.70205ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (454.628477ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (980.421846ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (140.155599ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (492.776074ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (263.12435ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (188.594668ms) +✔ H13: a retry with the same request ID and different text is refused (182.391217ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (538.187031ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (273.918773ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (179.054472ms) +✔ 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.012765ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (356.263253ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1039.146864ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (276.709162ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (351.441ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.714904ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (708.126457ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (593.666243ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (717.999775ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2903.895873ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (638.504156ms) +✔ no shim from this file's tests is left running for the after hook (27.654466ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (10.068213ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (3.223267ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.681608ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.767035ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.770841ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.507822ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (1.336156ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.590185ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.919572ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.019521ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.088435ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.43956ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.989277ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (10.94782ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.387517ms) +✔ F9: registrations never add or redirect a root (2.43844ms) +✔ F10: a header cwd naming another project is refused (4.976992ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.837569ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (6.384143ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (3.347304ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (2.233973ms) +✔ F13: compaction is a marker in place, then the retained content (1.213515ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (728.150222ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.58156ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.678211ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.87234ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.201858ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.32746ms) +✔ every page and cursor is a valid CHAT-01 record (1129.674417ms) +✔ the engine pin holds for the installed package (2.756258ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (391.939135ms) +✔ 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) (778.412749ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (345.643611ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (491.582144ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (455.997214ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (237.209527ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (23.510506ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (348.64816ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (200.99027ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (300.8304ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (465.232971ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1879.757838ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (183.381888ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (271.350885ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (185.728954ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (177.703684ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (354.399412ms) +✔ N10: fake conformance (33.179841ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (439.619822ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (273.292515ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (270.659321ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (463.884104ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (500.680671ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (521.389878ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (216.293018ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (360.367164ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (601.690118ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (500.095722ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (492.967391ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (200.434664ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (140.855633ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (134.081033ms) +✔ 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 (172.994486ms) +✔ 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 (204.791797ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60763.569546 diff --git a/agents/darkwing/work/queue-51-review/r1/mut/recafter-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/recafter-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/recafter-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/recbefore-conversation.txt b/agents/darkwing/work/queue-51-review/r1/mut/recbefore-conversation.txt new file mode 100644 index 00000000..5411e868 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/recbefore-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (209.023239ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (28.501409ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (16.928793ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (345.332889ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (376.292217ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (244.43726ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (186.078105ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (196.259965ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.574163ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12009.315853ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39606.196357ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (264.669207ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (146.725732ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (634.324063ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (278.808102ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (336.075936ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (98.141661ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (191.503308ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (251.562147ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (700.831469ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (180.587221ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (90.831246ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (70.78589ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.438358ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.067925ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.814725ms) +✔ no shim from this file's tests is left running for the after hook (25.336076ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (908.577883ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (443.14461ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (789.476911ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3042.321011ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (721.844983ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (877.179919ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (772.723621ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5628.006753ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (715.45386ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (762.654192ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (636.144297ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (518.788185ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (307.058516ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (412.652473ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3258.431321ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (41.758956ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (326.208841ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (278.129596ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (222.149044ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (556.629824ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (478.414362ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1129.461193ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (642.462135ms) +✔ 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) (472.043227ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (151.430334ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2882.080105ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (478.37034ms) +✔ R9: a release that ended unavailable is tried again on recover (613.4525ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (567.48385ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (429.933761ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (522.593419ms) +✔ no shim from this file's tests is left running for the after hook (24.468395ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (205.294303ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (169.39229ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (176.795906ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (256.977267ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (177.37561ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.476584ms) +✔ 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.774276ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (286.601435ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (205.253036ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (181.797842ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (208.77183ms) +✔ 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.751246ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.188054ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (206.862514ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (221.645576ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (243.309349ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (204.370354ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.453523ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.266851ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.336446ms) +✔ 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.692093ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.395899ms) +✔ 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.463989ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.713117ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.361184ms) +✔ 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.341343ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.104422ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (241.29776ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (234.86167ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (276.447ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (760.71782ms) +✔ H4: self-takeover is refused (180.027128ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (427.819747ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1030.360574ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (173.900785ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (540.860752ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (286.128911ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (296.275327ms) +✔ H13: a retry with the same request ID and different text is refused (280.263798ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (735.453846ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (301.871314ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (214.681817ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (928.532345ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (377.997222ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1044.178359ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (207.051235ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (346.239759ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.678139ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (678.08095ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (570.251402ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (674.58617ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2854.618783ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (770.01799ms) +✔ no shim from this file's tests is left running for the after hook (26.042562ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.254353ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.842699ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.793311ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.523211ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.3292ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.875343ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.836371ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.552607ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.341734ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.095666ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.515608ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.829411ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.83844ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.725492ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.014105ms) +✔ F9: registrations never add or redirect a root (1.909553ms) +✔ F10: a header cwd naming another project is refused (4.13566ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.704291ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.151708ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.091781ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.427783ms) +✔ F13: compaction is a marker in place, then the retained content (0.848958ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (512.478113ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.423569ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.351464ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.134137ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.102525ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.849827ms) +✔ every page and cursor is a valid CHAT-01 record (782.762579ms) +✔ the engine pin holds for the installed package (1.95636ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (266.1676ms) +✔ 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) (520.489398ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (263.555237ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (452.568355ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (394.847446ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (216.702949ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.10148ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (329.334887ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (192.782797ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (284.241348ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (469.069358ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1840.097953ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (296.535228ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (378.172619ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (270.557118ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (288.460086ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (371.466172ms) +✔ N10: fake conformance (32.308838ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (459.286511ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (289.37337ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (306.981465ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (385.037454ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (454.517447ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (528.700632ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (247.044028ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (365.246989ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (664.22667ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (502.299093ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (450.196256ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (207.505298ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (177.524159ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (156.19215ms) +✔ 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 (197.014994ms) +✔ 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.327964ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 59623.249872 diff --git a/agents/darkwing/work/queue-51-review/r1/mut/recbefore-shims-left.txt b/agents/darkwing/work/queue-51-review/r1/mut/recbefore-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/recbefore-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r1/mut/run.sh b/agents/darkwing/work/queue-51-review/r1/mut/run.sh new file mode 100755 index 00000000..df708299 --- /dev/null +++ b/agents/darkwing/work/queue-51-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/r51a/muttmp/mut-$m DOCKER_HOST=unix:///nonexistent.sock +mkdir -p $TMPDIR +M=~/darkwing-scratch/r51a/mut; cd ~/darkwing-scratch/r51a/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-51-review/r1/mut/summary.txt b/agents/darkwing/work/queue-51-review/r1/mut/summary.txt new file mode 100644 index 00000000..77cd3f66 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/mut/summary.txt @@ -0,0 +1,14 @@ +start 2026-10-10T05:57:51Z +norecover rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +nostartfree rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +nostartstopped rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +closekill rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +nopush rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +recafter rc 0 ℹ pass 177 ℹ fail 0 shims-left 0 +recbefore rc 0 ℹ pass 177 ℹ fail 0 shims-left 0 +noseat rc 0 ℹ pass 177 ℹ fail 0 shims-left 0 +nodedupe rc 0 ℹ pass 177 ℹ fail 0 shims-left 0 +nolookup rc 0 ℹ pass 177 ℹ fail 0 shims-left 0 +nodelete rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +closeproof rc 0 ℹ pass 177 ℹ fail 0 shims-left 0 +end 2026-10-10T06:09:58Z diff --git a/agents/darkwing/work/queue-51-review/r1/out/node-control-board.txt b/agents/darkwing/work/queue-51-review/r1/out/node-control-board.txt new file mode 100644 index 00000000..6631aec5 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/out/node-control-board.txt @@ -0,0 +1,132 @@ +✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (5.586865ms) +✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.262778ms) +✔ completed smoke replies and ordinary questions are idle, not human blockers (0.798889ms) +✔ only an explicit first-line input request makes a finished reply waiting (0.160632ms) +✔ tool activity, user text, errors and unfinished turns override attention text (0.155052ms) +✔ STOP access failure is unknown, not absence, under a non-root identity (36.81718ms) +✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (3.393131ms) +✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.552446ms) +✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (8.012271ms) +✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.964018ms) +✔ server rescans connector discovery and refuses HTTP reply without transport (38.137708ms) +✔ connector session links and linked directories are not read (1.121668ms) +✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (2.582823ms) +✔ CLI print uses the relaunch notice instead of old current preview (71.746346ms) +✔ connector owner and fixed task never inherit a native relaunch notice (3.101978ms) +✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (2.470433ms) +✔ loadConfig: missing file throws ConfigError (1.695168ms) +✔ loadConfig: invalid JSON throws ConfigError (0.369969ms) +✔ loadConfig: missing dataRoot throws ConfigError (0.299563ms) +✔ loadConfig: relative dataRoot throws ConfigError (0.343556ms) +✔ loadConfig: valid config returns dataRoot (0.970812ms) +✔ findNewestSession: picks the newest by mtime among two files (0.60517ms) +✔ findNewestSession: finds files in nested subdirectories (0.314342ms) +✔ findNewestSession: returns null for a missing dir (0.141329ms) +✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.78458ms) +✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.763018ms) +✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.632142ms) +✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.222871ms) +✔ deriveState: full state table (0.198899ms) +✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.354151ms) +✔ rule: newest entry is a tool result with no assistant text after it is working (0.331179ms) +✔ rule: a finished ordinary turn is idle, even if it says your move (0.277842ms) +✔ task: the first user message of the session, from text blocks (0.295189ms) +✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.271821ms) +✔ task: no user message in the log means null (shown as unknown), never a guess (0.317005ms) +✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.427832ms) +✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.527602ms) +✔ scan: the written record carries task, workspace and activeProject (0.499977ms) +✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.606214ms) +✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.400209ms) +✔ registration: a record whose pid is gone is stale; derived values win, sources say derived, registered stays with alive false; a pid the probe cannot decide is not stale; pidAlive itself (1.110048ms) +✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.456585ms) +✔ loadRegistrations: a missing seatsDir gives empty lists (0.18718ms) +✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.778026ms) +✔ matchRegistration: matches by sessionsDir, and by realpath through a symlink; sessionsDir null never matches; same seat name with a different sessionsDir does not match (fleet vs repo darkwing) (0.365519ms) +✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (1.749136ms) +✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.370463ms) +✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.707932ms) +✔ scanAgent: ageSeconds is computed from the injected now (0.379478ms) +✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.212138ms) +✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.368488ms) +✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.418657ms) +✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (1.77764ms) +✔ scan: relative boardDir throws ConfigError (0.12319ms) +✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (70.377752ms) +✔ CLI: missing config exits 2 with a refused: message (56.527574ms) +✔ CLI: unknown command exits 2 (54.11048ms) +✔ CLI: unknown --liveness value exits 2 (54.461178ms) +✔ panesRunPi: true when any trimmed line equals 'pi' (0.183638ms) +✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.083377ms) +✔ tmuxIsAlive: a pane running pi is alive (0.17755ms) +✔ tmuxIsAlive: session exists but pi has exited is not alive (0.070114ms) +✔ tmuxIsAlive: no such tmux session is not alive (0.058706ms) +✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.059245ms) +✔ tmuxIsAlive: passes -L only when a socket is given (0.094457ms) +✔ parsePanes: one pane per line, command and optional tab-separated path (0.077013ms) +✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.070768ms) +✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.089845ms) +✔ loadSeen: missing file returns {} (0.135903ms) +✔ loadSeen: invalid JSON throws ConfigError (0.187184ms) +✔ loadSeen: a JSON array throws ConfigError (0.168968ms) +✔ loadSeen: a non-string value throws ConfigError (0.162992ms) +✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.302686ms) +✔ markSeen: seen false deletes the key (0.238168ms) +✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.188296ms) +✔ markSeen: project containing '/' throws ConfigError (0.109128ms) +✔ markSeen: non-boolean seen throws ConfigError (0.098665ms) +✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.265547ms) +✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.21637ms) +✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.241375ms) +✔ scanAgent: an error-state session with a matching mark is seen (0.211697ms) +✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.466707ms) +✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.184214ms) +✔ taskSetBy: a registered task carries the record's setter; a record without the field (pre-#1511) reads unknown; the value is not copied into registered (0.580839ms) +✔ taskSetBy: null whenever the task shown is not the registered one: no registration, an empty registered task, a stale registration; the field is always present (0.660861ms) +✔ taskSetBy: scan() reads the field from disk through the seat package (bounded there), writes it to the per-agent record and index, and an invalid on-disk value is a registrationError, never a row value (0.985638ms) +✔ isLoopbackHost: recognizes loopback hosts (1.339261ms) +✔ isLoopbackHost: rejects non-loopback hosts (4.632347ms) +✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.5214ms) +✔ startServer: serves page, healthz, and a rescanning /api/board (41.068414ms) +✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (7.268998ms) +✔ startServer: /api/board returns 500 JSON with an error field when scan throws (3.44951ms) +✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (58.545194ms) +✔ CLI: serve rejects a non-numeric --port with exit 2 (56.356369ms) +✔ CLI: scan still works after the async cli refactor (54.657629ms) +✔ CLI: live serve prints its URL and answers /healthz (61.267683ms) +✔ page.html: esc() escapes every HTML-significant character (0.647712ms) +✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (7.801657ms) +✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (1.859505ms) +✔ POST /api/seen with invalid JSON returns 400 (2.773471ms) +✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (2.038722ms) +✔ POST /api/seen with a missing agent returns 400 (1.275955ms) +✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.671451ms) +✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (50.857401ms) +✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.316609ms) +✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.263972ms) +✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.163403ms) +✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.136867ms) +✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.321441ms) +✔ page.html: task and active project cells show their source via sourceTag(); the detail has a Registered row via registeredText(); SOURCE_LABEL maps registration to registered; every dynamic value in sourceTag/fromSource/registeredText is escaped (0.559812ms) +✔ POST /api/reply: runs agent-send.sh with -s from the registration, -S :control-board, -m text plus the fixed trailer, no -L on the default socket, MOSAIC_TMUX_SOCKET stripped; answers delivered with the exit code and both streams (26.992031ms) +✔ POST /api/reply: a registration with a tmux socket adds -L (26.124755ms) +✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (27.797958ms) +✔ POST /api/reply: refusals before the tool runs: empty or blank or long text 400, unknown row 404, no registration 409, stale registration 409, no tmux session 409, bad JSON 400; the tool is never called (14.743214ms) +✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.666102ms) +✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.177529ms) +✔ page.html: the reply box appears only where canReply() holds (live registration with a tmux session), the detail has a Reply row, the submit posts JSON to /api/reply, receipts and drafts survive a refresh, and every receipt value is escaped (0.570416ms) +✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.387793ms) +✔ page.html: the task cell and detail show who set a registered task via setByTag()/setByText(), both escaped, only from rec.taskSetBy; the reply gate does not read it (0.293473ms) +✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (7.668789ms) +✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (33.703336ms) +✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (5.45161ms) +✔ every refusal code the reader can raise has an HTTP status (0.887537ms) +✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (47.505486ms) +ℹ tests 124 +ℹ suites 0 +ℹ pass 124 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 665.164947 diff --git a/agents/darkwing/work/queue-51-review/r1/out/node-conversation.txt b/agents/darkwing/work/queue-51-review/r1/out/node-conversation.txt new file mode 100644 index 00000000..57048edb --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/out/node-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (218.804214ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.911331ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.165421ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (370.962647ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (416.925411ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (263.387001ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (806.963733ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (345.543937ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.368776ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (16899.02663ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (36913.686867ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (259.906157ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (142.490266ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (678.775294ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (315.896411ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (372.751735ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (122.874741ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (228.551179ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (353.413402ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1315.5471ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (352.136133ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (128.292952ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (87.603895ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.355281ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.083452ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.782549ms) +✔ no shim from this file's tests is left running for the after hook (25.150294ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (990.323369ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (1166.874838ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (962.049063ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2920.425818ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2607.014243ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (1656.8339ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (634.589021ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5721.819757ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1066.355049ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (1062.871572ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (1055.343871ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (1375.72719ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (795.874851ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (926.883571ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3431.056835ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (27.004769ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (618.444407ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (745.79076ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.18763ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (572.625399ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (704.176414ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1593.383259ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (768.973491ms) +✔ 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) (454.76837ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (154.087111ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2816.149388ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (784.281005ms) +✔ R9: a release that ended unavailable is tried again on recover (878.785156ms) +✔ R10: close releases nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (518.130596ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID (597.636188ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (360.043259ms) +✔ no shim from this file's tests is left running for the after hook (25.75809ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (195.92535ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (203.745296ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (190.69069ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (289.643707ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (211.634307ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.534317ms) +✔ 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.229381ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (898.569263ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (391.986137ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (273.345582ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (301.435321ms) +✔ 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 (271.989606ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.035467ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (235.627831ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (238.607169ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (288.731468ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (231.265994ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.400489ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.269198ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.353682ms) +✔ 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.485844ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.010054ms) +✔ 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.623737ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.924593ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.429257ms) +✔ 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.331292ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.09941ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (231.026804ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (255.624719ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (298.444377ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1496.35036ms) +✔ H4: self-takeover is refused (375.587334ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (583.691439ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1217.310432ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (366.558163ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1150.281073ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (375.873979ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (1519.421694ms) +✔ H13: a retry with the same request ID and different text is refused (1197.542723ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (1619.646821ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (369.454556ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (243.835162ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1421.998213ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (716.161934ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1003.911975ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (285.726886ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (410.279472ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.621912ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (735.926022ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (659.896928ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (863.129021ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3300.165641ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (787.52205ms) +✔ no shim from this file's tests is left running for the after hook (28.933813ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.758709ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.81153ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.713368ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.645176ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.494149ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.943443ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.820509ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.586397ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.183168ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.199059ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.658835ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (7.463174ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (8.564237ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.456153ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.800976ms) +✔ F9: registrations never add or redirect a root (2.69105ms) +✔ F10: a header cwd naming another project is refused (5.285059ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.911374ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.682632ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.262703ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.48717ms) +✔ F13: compaction is a marker in place, then the retained content (1.66763ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (591.80274ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.638126ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.604673ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.735364ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.391532ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.261571ms) +✔ every page and cursor is a valid CHAT-01 record (892.728906ms) +✔ the engine pin holds for the installed package (1.913859ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (320.059015ms) +✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (549.260468ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (283.540152ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (489.779346ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (443.250942ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (240.140137ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.035764ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (949.80609ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (340.4963ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (425.850492ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (645.935287ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2014.95677ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (247.550815ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (322.594945ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (224.243292ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (1307.987522ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (2265.176074ms) +✔ N10: fake conformance (33.210869ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (821.720277ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (372.176825ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (331.364173ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (585.525295ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (620.88861ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (737.598278ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (251.269044ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (450.825373ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (1075.742264ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (705.119533ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (738.639225ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (305.850133ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (228.091522ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (259.420614ms) +✔ 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 (296.360039ms) +✔ 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 (292.266113ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 64443.020238 diff --git a/agents/darkwing/work/queue-51-review/r1/out/node-queue.txt b/agents/darkwing/work/queue-51-review/r1/out/node-queue.txt new file mode 100644 index 00000000..8dc0de7c --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/out/node-queue.txt @@ -0,0 +1,158 @@ +✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1389.796984ms) +✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1181.690577ms) +ℹ git commit -e: index.lock free during the editor +✔ F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update (1097.136238ms) +ℹ git commit -e -- src.txt: index.lock held during the editor +✔ F1: step 8 with index.lock held exits 3, and ordinary commits stay refused until the printed command runs (1233.467919ms) +✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1185.009399ms) +✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1077.546446ms) +✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1440.996801ms) +✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1034.618271ms) +✔ F1: a shared-index change during the procedure is not committed (1181.144691ms) +✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1138.734749ms) +✔ F1: a missing or a different hook refuses (856.300494ms) +✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1388.665187ms) +✔ F1: the canary refuses a hook that git would not run (743.885248ms) +✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1055.548902ms) +✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (1042.454814ms) +✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (1043.638082ms) +✔ bootstrap: the archived tests and validator run outside any repository (895.297957ms) +✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (910.498606ms) +✔ general: a queue write after the snapshot is not committed (1311.166744ms) +✔ general: a snapshot whose log does not extend the base refuses (1015.316486ms) +✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (178.868908ms) +✔ general: environment overrides, a linked worktree and usage (616.075281ms) +✔ general: a queue path staged before the run refuses at step 1 (713.817747ms) +✔ general: HEAD's queue tests failing in the archive refuse (927.604796ms) +✔ genesis document serializes deterministically and replays (4.043583ms) +✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.188771ms) +✔ a tampered result, receipt or viewSha fails replay (2.217186ms) +✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.151392ms) +✔ add: defaults for an ordinary seat, privileged extras, refusals (3.854808ms) +✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (5.088106ms) +✔ matrix: release, review round, changes requested and waiting-on-jason (12.141306ms) +✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (9.504502ms) +✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (19.576444ms) +✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.418158ms) +✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1370.165577ms) +✔ matrix: blocked keeps the claim and returns only to previousState (3.80596ms) +✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.621675ms) +✔ field edits: who may change what (5.278261ms) +✔ set issues keeps a logged narrowing of closes (N10) (3.514273ms) +✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.974585ms) +✔ every accepted text renders to nine cells on every row (N8) (26.454896ms) +✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.596956ms) +✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.667992ms) +✔ replay holds every op id to the caller's rule (N11) (5.278898ms) +✔ note: owner, listed reviewer or privileged; empty clears (1.194797ms) +✔ assign moves the claim with the owner; done clears it (2.61094ms) +✔ render is byte-stable and escapes pipes (0.525506ms) +✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.599918ms) +✔ next: resume, then review, then start, then wait, then nothing; lowest id first (18.25214ms) +✔ canonical args make a retry's identity independent of list order (0.309413ms) +✔ manifests, headings and blob ids (0.519901ms) +✔ the migration map: one queue-map block, exact keys (0.746341ms) +✔ every call but `queue` reaches the seat CLI exactly as before A2 (616.379592ms) +✔ `queue` reaches the queue CLI with the rest of the arguments (186.759494ms) +✔ the pre-A2 fixture is the script A2 changed (0.296873ms) +✔ acquire publishes the record by link; release removes only its own lock (12.584693ms) +✔ a kill between the temp write and the link leaves no lock (55.867061ms) +✔ a short or failed temp write refuses and leaves no lock and no temp (7.788643ms) +✔ a link error other than EEXIST refuses (6.545932ms) +✔ an error after the link releases the lock: unreadable gate, failing temp stat (22.087003ms) +✔ a release that fails on a gate path is reported, never a stack trace (P1) (41.753521ms) +✔ a paused holder: another writer waits 10 s, then refuses naming it live (10109.663598ms) +✔ two concurrent unlockers: the second refuses on the gate (46.625541ms) +✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (22.352053ms) +✔ a writer publishing during an unlock, gate first: the writer releases and refuses (19.236607ms) +✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (23.223953ms) +✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (21.076123ms) +✔ the same pid and start on a different boot is mismatch (0.88126ms) +✔ a foreign host is unknown whatever the local pid says; unlock refuses (72.421331ms) +✔ unreadable /proc: classification is unknown and acquire refuses (0.791156ms) +✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.208617ms) +✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (21.218559ms) +✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (28.75555ms) +✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (12.700687ms) +✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (46.862569ms) +✔ the migration map validates and renders the golden genesis table (3.217631ms) +✔ the marked QUEUE.md holds every row and parked item between its markers (1.250292ms) +✔ map-check reports each kind of drift (4.208229ms) +✔ a request posts once as the requester; a retry sends nothing (878.443611ms) +✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (5054.649739ms) +✔ the pre-send checks: GET user must name the requester, under the deadline (1663.402145ms) +✔ the lead's request refuses a token for login sage (1060.892158ms) +✔ the credential file: the seat's own, 0600, no symlink, never the shared default (1077.725104ms) +✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2465.733683ms) +✔ a same-op retry after a kill sends nothing, even with a stale view (3533.3845ms) +✔ a held lock at the outcome exits 3 and names what the transport said (759.205559ms) +✔ late outcomes: after an abandon, and after a resolve with the same or another id (2605.442733ms) +✔ resolve checks the comment: issue, markers, round, candidate and author (1678.294203ms) +✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (733.784554ms) +✔ validateRow checks a request round's shape, which every replayed entry must keep (509.957669ms) +✔ request, changes, a new candidate, approval: every round pinned; no review files (1804.396471ms) +✔ a row with no reviewers opens a round that posts nothing (1228.471735ms) +✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1329.730153ms) +✔ semantics: v1 entries replay as before; review entries need v2 (479.118556ms) +✔ set reviewers refuses the row's owner (2026-09-28) (371.182165ms) +✔ the owner records no verdict, even as a listed reviewer (536.018636ms) +✔ a request comment over the length limit is not sent (614.417366ms) +✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (650.135513ms) +✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2605.7275ms) +✔ genesis: refusals before anything is written (454.271346ms) +✔ genesis: the map must be committed, well formed, with committed briefs and seats (650.932601ms) +✔ genesis: markers, a stray witness, once only; a retry returns the receipt (603.223192ms) +✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (435.069321ms) +✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (706.593146ms) +✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (879.751507ms) +✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (801.687831ms) +✔ a retried add returns the id it first allocated, after reassignment and after done (1227.56586ms) +✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (762.087941ms) +✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1328.272583ms) +✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (890.678328ms) +✔ add, set reviewers and assign refuse the row's owner as a reviewer (542.458021ms) +✔ the working-brief check: a changed working copy refuses the start and flags next (823.809554ms) +✔ next: resume first, then nothing for an idle seat; needs a seat (327.957489ms) +✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (717.199235ms) +✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1345.102045ms) +✔ a hand edit to queue.json refuses every verb, reads included (658.069494ms) +✔ verify and render --check leave bytes and mtimes unchanged (474.221591ms) +✔ render is byte-stable across runs and repositories (372.19331ms) +✔ snapshot and verify --snapshot (865.20262ms) +✔ usage errors exit 4 (573.197163ms) +✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (352.725362ms) +✔ a rename failure: nothing visible, temp removed (196.836118ms) +✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (256.668705ms) +✔ a directory fsync failure, then sync names the op (404.527109ms) +✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (234.522579ms) +✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (233.457901ms) +✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (253.583501ms) +✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (224.016252ms) +✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (237.009139ms) +✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (241.916726ms) +✔ unlock prints a swapped gate's warning on stderr, the result on stdout (175.898457ms) +✔ a view write that fails keeps the op and reports a stale view (211.383429ms) +✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (710.364612ms) +✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (719.858141ms) +✔ SIGKILL after the witness, before the view: the stale refusal names the op (678.923203ms) +✔ SIGKILL after the view, before the receipt: the retry returns the receipt (678.626817ms) +✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (294.217939ms) +✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1127.360624ms) +✔ a deleted witness: refused after the locked recheck; accept-history records it absent (374.919529ms) +✔ a header edit during a write: the op stands, the view write is skipped with a warning (232.83562ms) +✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (236.714153ms) +✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (242.755571ms) +✔ a writer paused before and after the witness rename: readers see a tail, then a match (253.636043ms) +✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (616.338271ms) +✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (769.433999ms) +✔ the platform check refuses other filesystems (177.159991ms) +✔ tmpfs passes only a test layer that allows it (N5) (281.308619ms) +✔ unlock keeps a multi-line lock record on stdout (P3) (236.93142ms) +ℹ tests 148 +ℹ suites 0 +ℹ pass 148 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 31718.288606 diff --git a/agents/darkwing/work/queue-51-review/r1/out/node-webui.txt b/agents/darkwing/work/queue-51-review/r1/out/node-webui.txt new file mode 100644 index 00000000..10c7bc39 --- /dev/null +++ b/agents/darkwing/work/queue-51-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 (4268.095595ms) +Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286} +✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4794.618428ms) +✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5639.895725ms) +✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (267.711463ms) +✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (74.272149ms) +✔ bus refusals map to statuses and never carry a path or a stack (46.677358ms) +✔ humanCall speaks the human transport protocol without blocking the server (2235.534578ms) +✔ busReader takes --business, else the live bus host, else refuses (359.59682ms) +✔ humanCall kills a transport that runs past its timeout (528.445903ms) +✔ serve refuses a --business value that is not a business id (178.986402ms) +✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4256.709408ms) +✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1914.313831ms) +✔ conversation view: a newer session with no readable history keeps the marker (1693.487684ms) +✔ conversation view: seats without history say so and offer no reply (981.594057ms) +✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4633.52603ms) +✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54880.02174ms) +✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (4632.998974ms) +✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22810.59787ms) +✔ loopback host and board origin fail closed (8.433401ms) +✔ real board fixture passes through WebUI; assets and isolated seen/reply work (116.584083ms) +✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (79.819591ms) +✔ unreachable board reports URL; CLI rejects unsupported options (964.444371ms) +ℹ tests 22 +ℹ suites 0 +ℹ pass 22 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 55268.927907 diff --git a/agents/darkwing/work/queue-51-review/r1/out/summary.txt b/agents/darkwing/work/queue-51-review/r1/out/summary.txt new file mode 100644 index 00000000..8c57bd93 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r1/out/summary.txt @@ -0,0 +1,16 @@ +start 2026-10-10T05:53:20Z shims=0 units=0 +node-conversation exit=0 ℹ pass 177 ℹ fail 0 shims-left=0 units=0 +node-webui exit=0 ℹ pass 22 ℹ fail 0 shims-left=0 units=0 +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-10T05:56:53Z shims-left=0 units=1 +DONE +node-control-board exit=0 ℹ pass 124 ℹ fail 0 +node-queue exit=0 ℹ pass 148 ℹ fail 0 diff --git a/agents/darkwing/work/queue-51-review/r1/out/test-auth.txt b/agents/darkwing/work/queue-51-review/r1/out/test-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/darkwing/work/queue-51-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-51-review/r1/out/test-conductor.txt b/agents/darkwing/work/queue-51-review/r1/out/test-conductor.txt new file mode 100644 index 00000000..62afc513 --- /dev/null +++ b/agents/darkwing/work/queue-51-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-51-review/r1/out/test-config.txt b/agents/darkwing/work/queue-51-review/r1/out/test-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/darkwing/work/queue-51-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-51-review/r1/out/test-discord.txt b/agents/darkwing/work/queue-51-review/r1/out/test-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/darkwing/work/queue-51-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-51-review/r1/out/test-extension-package.txt b/agents/darkwing/work/queue-51-review/r1/out/test-extension-package.txt new file mode 100644 index 00000000..49d84664 --- /dev/null +++ b/agents/darkwing/work/queue-51-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: 1322106 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-51-review/r1/out/test-foundation.txt b/agents/darkwing/work/queue-51-review/r1/out/test-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/darkwing/work/queue-51-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-51-review/r1/out/test-queue.txt b/agents/darkwing/work/queue-51-review/r1/out/test-queue.txt new file mode 100644 index 00000000..ccce8122 --- /dev/null +++ b/agents/darkwing/work/queue-51-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/r51a/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-51-review/r1/out/test-release-docker.txt b/agents/darkwing/work/queue-51-review/r1/out/test-release-docker.txt new file mode 100644 index 00000000..7202b85d --- /dev/null +++ b/agents/darkwing/work/queue-51-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-51-review/r1/out/test-release.txt b/agents/darkwing/work/queue-51-review/r1/out/test-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/darkwing/work/queue-51-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-51-review/r1/out/test-task.txt b/agents/darkwing/work/queue-51-review/r1/out/test-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/darkwing/work/queue-51-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-51-review/review-r1.md b/agents/darkwing/work/queue-51-review/review-r1.md new file mode 100644 index 00000000..b7fd4c39 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/review-r1.md @@ -0,0 +1,178 @@ +# Row 51, cohort release follow-ups, round 1 review (Darkwing) + +Issue #1537, request comment 27068, notes comment 27069, queue revs +298-301 (`d947ec11`). Brief: `docs/plans/2026-10-10_cohort-release-follow-ups.md`, +section "Cohort release follow-ups: engine exit, crash window and close". +Base `bbb2167f`; I ran it on `d947ec11`, which adds only the two +queue files. Candidate manifest sha256 +`2da94e3bcb64afbc5f70fe1cc888393a32b7988afd1cf2cd977d762b47c6ebf3`, +5 files: `controller.mjs`, `cohort.test.mjs`, `ctrl-child.mjs`, the README +and Dewey's `evidence.md`. `cohort.mjs` and `shim.mjs` are unchanged. + +Verdict: **changes**, for one item. The recover release runs before the +confirmation check, and the packet and the README both say it runs inside +a confirmed `recover`. The code is safe either way: every release still +needs a claim recorded `stopped` on a cohort proof, and the shim refuses +while its engine runs. But the packet's K6 line is the part of the +evidence that says which claim-protocol rules hold, and it's wrong. Fix +the order or fix the words; I'd fix the order. Everything else is right: +the crash window closes in both shapes (R7, R8), the retry works (R9), +close sends no signal after a proven stop (R11), and a boot proof is never +released (R12). Dewey's ten mutants reproduce. + +## Method + +- Detached worktree at `d947ec11`, the five candidate files copied from a + snapshot of the canonical tree, then `sha256sum -c`: 5 OK. A second + worktree for the mutants; its five files check OK after all runs + (`r1/mut/manifest-after.txt`). +- I read the diff in full, then `#recover`, `#releaseScope`, + `#releaseListed`, `start()`, `close()`, `#escalate`'s stopped path and + `release()` in `controller.mjs`, `systemdUnits.lookup` and + `systemctlShow` in `cohort.mjs`, `checkConfirmation` and `recover` in + `docs/plans/chat-01/check.mjs`, and R1, R6-R12. +- 12 mutants (`r1/mut/mutate.py`, `run.sh`), the whole conversation suite + each, own TMPDIR each, run after the gate finished. After each run the + runner lists any shim left under that TMPDIR. No mutant left one. + +Node v26.8.1, `TMPDIR=~/darkwing-scratch/r51a/tmp`, `gate.sh` with +`DOCKER_HOST=unix:///nonexistent.sock`, 05:53:20Z to 05:56:53Z. + +## Suites + +| Suite | Result | +|---|---| +| conversation | 177/0 | +| webui | 22/0 | +| control-board | 124/0 | +| queue (node) | 148/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 earlier gates. Dewey's 98/0 ran them. No `mosaic-chat-*` unit was listed +after the conversation suite. The one unit counted at the gate's end was +gone when I looked again, and none was listed after the mutants. + +## The recover release runs before the confirmation (required) + +`#recover` (`controller.mjs`, from line 1621): + +``` +if (b.state !== "stopped" || ... || !this.#stopped(s)) return refused("stop-proof"); +await this.#releaseListed("recover", this.claim); // 1625 +... pins, refused(ENGINE_PIN_MISMATCH) ... +... session leaf and branch, refused("target") ... +if (!this.#checkConfirmation(r, c, "recover")) return refused("confirmation"); // 1635 +``` + +So a `recover` with no confirmation, a stale one (H17), changed pins or a +moved leaf still retries the release, then refuses. The packet says "The +recover release runs inside a confirmed `recover`, after its `stop-proof` +check". The README says the retry happens on "the next `start` or +confirmed `recover` (R9)". Neither is what the code does. + +Why it doesn't hurt much: the release only ever acts on a proven-stopped +cohort, and close does the same with no confirmation at all. Why I still +want it fixed: + +- The "Claim-protocol rules touched" section is how a reviewer checks the + row against CHAT-00. A wrong K6 line there is the kind of record we + commit and then rely on. +- With the order as is, a refused command has a side effect: an + `evidence.releases` entry, a push to every observer, and up to 3 s of + `systemctl show` polling before the refusal goes back. +- No test pins either order. `recafter` (the release moved below the + confirmation check) and `recbefore` (moved above the stop-proof check) + both pass 177/0. + +What I'd accept, either one: + +1. Move the `#releaseListed` call below `#checkConfirmation`, before the + acquire. The packet and README are then true as written. Add an + assertion that kills `recafter`: in R9, before the confirmed recover, an + unconfirmed `recover` for the same stop refuses `confirmation` and + `evidence.releases` is still the one `unavailable` entry. +2. Keep the order and correct the packet's K6 line and the README to say + any `recover` that passes the stop-proof check retries the release, + before the pins, target and confirmation checks. + +I recommend 1. A retry on recover only matters for a recovery that's +about to happen, and the next `start` covers everything else. + +## The rest of the change + +`#releaseListed` reads right. Two concurrent callers that both find a +failed earlier result can't both send a request: the first deletes and +re-memoizes, the second sees the map changed, skips the delete, and +`#releaseScope` hands it the new promise. `nodelete` (the delete removed, +so `#releaseScope` returns the old result) is killed by R9. + +`systemdUnits.lookup` can't throw: `spawnSync` with a timeout reports a +failure through `status`, and `lookup` returns `unknown` then. An injected +`units` that throws would reject `start()` before the acquire, which +writes nothing. Fine. + +The free path in `start()` releases before the pin and target checks and +before the acquire, so a start that then refuses `engine-pin-mismatch` +still releases the prior proven-stopped scope. That's correct: the proof +doesn't depend on the next launch. It also runs without holding the pair. +A second process on the same claim gets `unavailable` from `hello` and +records it, which is harmless. + +Close keys the skip on `b.state === "stopped"`. I checked the routes to +that state: `#escalate` (line 1562) is the only write, after the verifier +accepted the proof and `#claimFinish` recorded it. Line 1020 leaves +`stopped` alone, and `release()` (line 1689) finishes an unlaunched +reservation on a `no-unit` proof, which `#releaseScope` never accepts. + +## Mutants + +| Mutant | Change | Conversation | Result | +|---|---|---|---| +| norecover | recover releases nothing (Dewey's) | 176/1 | killed: R9 | +| nostartfree | no release on the free path (Dewey's) | 176/1 | killed: R7 | +| nostartstopped | no release on classify's `stopped` branch (Dewey's) | 176/1 | killed: R8 | +| closekill | close signals after a proven stop (Dewey's) | 176/1 | killed: R11 | +| nopush | the release entry isn't pushed (Dewey's) | 176/1 | killed: R1 | +| nodelete | `#releaseListed` never drops an earlier result | 176/1 | killed: R9 | +| recafter | the recover release moves below the confirmation check | 177/0 | survives: the required item above | +| recbefore | the recover release moves above the stop-proof check | 177/0 | survives: the guard in `#releaseScope` still needs a stopped cohort proof | +| noseat | the free path checks only the session head | 177/0 | survives: no test has a seat head naming another claim | +| nodedupe | the seat head's same-claim skip is dropped | 177/0 | survives: equivalent, the per-claim memo returns the first result | +| nolookup | `#releaseListed` sends a request for a unit already absent | 177/0 | survives: no test starts on a released claim and checks `evidence.releases` | +| closeproof | close skips the signal only while `#stopped()` still accepts the stop | 177/0 | survives: R10 calls close without `killEngine` | + +## Notes (not blocking) + +1. `closeproof` survives, and the packet makes a claim about exactly this + case: "A proof that stops verifying later (R10's withdrawn verifier) + doesn't bring the signal back". R10 calls `close()` without + `killEngine`. Passing `{ killEngine: true }` there and checking no + signal reaches the engine PID, as R11 does, would test it. +2. `noseat` survives. The seat-head branch handles a different session's + controller that died in the crash window on the same seat. One test + with two sessions on one seat would cover it; or say in the packet it's + untested. +3. `nolookup` survives. A resume after a stop whose release already + succeeded should record nothing. An assertion that + `evidence.releases` is empty after such a start (W8's shape) would pin + the lookup. +4. I agree with both of Dewey's follow-ups. The `still listed` retry can't + be faked without changing `cohort.mjs`, and the packet says why the R9 + path covers the same code. + +## 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.