diff --git a/agents/darkwing/work/queue-51-review/r2/candidate-manifest.sha256 b/agents/darkwing/work/queue-51-review/r2/candidate-manifest.sha256 new file mode 100644 index 00000000..b0ab19b1 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/candidate-manifest.sha256 @@ -0,0 +1,5 @@ +d7ae740247a7dbd26bdcadfe97e1acf70121911d6fed69f4fc2408f483279ef8 agents/dewey/work/queue-51/evidence.md +aaefe3cd02ea25dc00c31d0f94ef4c74544b9929614618511be294aa314de9ce packages/conversation/README.md +ae9cd6f800d57412ff04da7673bff6b62d506ec4ac46a15d351a45a5087a8953 packages/conversation/src/controller.mjs +6fada028358fbfe79f2a7dbda7fee8028142a833caed5bd3c2d4f00e861a19ec packages/conversation/tests/cohort.test.mjs +3409fcce6ebb0281b5e018983a005b0c23b8641ba69cd25ea8aec6c161870d3b packages/conversation/tests/ctrl-child.mjs diff --git a/agents/darkwing/work/queue-51-review/r2/gate.sh b/agents/darkwing/work/queue-51-review/r2/gate.sh new file mode 100755 index 00000000..4acda312 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/gate.sh @@ -0,0 +1,24 @@ +#!/bin/bash +export TMPDIR=~/darkwing-scratch/r51b/tmp DOCKER_HOST=unix:///nonexistent.sock +cd ~/darkwing-scratch/r51b/wt +O=~/darkwing-scratch/r51b/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 +for p in control-board queue; 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' ' ')" >> $O/summary.txt +done +echo DONE2 >> $O/summary.txt diff --git a/agents/darkwing/work/queue-51-review/r2/mut/closekill-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/closekill-conversation.txt new file mode 100644 index 00000000..c75eb8ce --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/closekill-conversation.txt @@ -0,0 +1,235 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (223.985151ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.620561ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.611922ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (378.285629ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (401.810846ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (232.363299ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (272.76975ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (213.831123ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.419176ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12111.695607ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (36888.868126ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (259.356201ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (151.467197ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (595.309814ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (282.201511ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (352.673758ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (106.123934ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (198.776097ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (277.897509ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (753.009748ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (187.941264ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (88.620161ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (76.730342ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.133234ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.076716ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.924155ms) +✔ no shim from this file's tests is left running for the after hook (24.047876ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1009.243932ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (466.569338ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (899.293837ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3003.173196ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (702.044369ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (813.442609ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (747.13105ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (6145.885984ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (948.334601ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (874.886798ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (607.817287ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (667.463841ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (319.923818ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (499.742365ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3217.777369ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (29.809023ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (355.017562ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (258.587156ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.332891ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (561.952407ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (482.894582ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1124.966495ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (645.829402ms) +✔ 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) (465.587129ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (152.348181ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2776.636806ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (514.17354ms) +✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (474.682607ms) +✖ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (2382.158392ms) +✖ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2616.321746ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (298.833085ms) +✔ no shim from this file's tests is left running for the after hook (23.52216ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (211.450717ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (217.2113ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (209.306043ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (290.875104ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (203.603086ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.449595ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (201.610268ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (378.238162ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (288.579559ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (208.957535ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (205.907514ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (211.057457ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.334455ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (205.403399ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (224.647774ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (242.201059ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (266.435397ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.384767ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.247695ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.339252ms) +✔ 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.481711ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.032327ms) +✔ 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.600962ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.71951ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.361179ms) +✔ 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.32064ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.116364ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (229.520948ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (259.705961ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (308.605307ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (857.137594ms) +✔ H4: self-takeover is refused (260.685225ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (515.268057ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1042.342631ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (300.993764ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (537.300747ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (254.811001ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (142.498836ms) +✔ H13: a retry with the same request ID and different text is refused (183.191576ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (554.415807ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (286.5008ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (195.883889ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (812.374021ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (341.901969ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (750.549857ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (256.124108ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (406.460934ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.658818ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (783.285315ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (588.026148ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (721.265872ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3179.697061ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (602.727506ms) +✔ no shim from this file's tests is left running for the after hook (23.131317ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.580767ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.698714ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.601182ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.152393ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.608096ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.391492ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (1.089866ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.373901ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.194578ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.601703ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.150193ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.950429ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.73455ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.494877ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.670162ms) +✔ F9: registrations never add or redirect a root (1.536725ms) +✔ F10: a header cwd naming another project is refused (3.728479ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.699051ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.113165ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.894503ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.326266ms) +✔ F13: compaction is a marker in place, then the retained content (0.836781ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (491.224916ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.766004ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.536594ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.423983ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.093858ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.182606ms) +✔ every page and cursor is a valid CHAT-01 record (752.026434ms) +✔ the engine pin holds for the installed package (1.936177ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (258.89728ms) +✔ 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) (510.669015ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (252.423303ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (524.98724ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (452.564003ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (246.867638ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.968745ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (431.254543ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (254.757881ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (336.268659ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (471.822751ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1880.34387ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (190.558905ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (279.717161ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (148.516544ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (216.086324ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (356.438655ms) +✔ N10: fake conformance (32.083433ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (400.421485ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (269.090395ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (283.314204ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (382.874514ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (339.723918ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (412.28814ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (250.56939ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (444.092197ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (919.349629ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (470.70348ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (480.686403ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (194.348212ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (200.131924ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (187.287538ms) +✔ 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 (150.490115ms) +✔ 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 (184.831315ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 175 +ℹ fail 2 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 57236.688204 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1049:1 +✖ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (2382.158392ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [ + + [ + + 2483563, + + 'SIGKILL' + + ] + + ] + - [] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1065:12) + 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: [ [ 2483563, 'SIGKILL' ] ], + expected: [], + operator: 'deepStrictEqual', + diff: 'simple' + } + +test at packages/conversation/tests/cohort.test.mjs:1081:1 +✖ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2616.321746ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [ + + [ + + 2484011, + + 'SIGKILL' + + ] + + ] + - [] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1105:10) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: true, + code: 'ERR_ASSERTION', + actual: [ [ 2484011, 'SIGKILL' ] ], + expected: [], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r2/mut/closekill-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/closekill-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/closekill-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/closeproof-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/closeproof-conversation.txt new file mode 100644 index 00000000..a4ef4c1d --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/closeproof-conversation.txt @@ -0,0 +1,211 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (212.615731ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.954294ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.172829ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (375.941814ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (399.276752ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (258.418955ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (327.362714ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (350.509686ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.708729ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12212.045852ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38082.591861ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (260.985979ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (143.524135ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (633.509338ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (268.965183ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (345.28464ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (114.490507ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (196.416052ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (271.662288ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (858.457464ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (288.898611ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (102.578258ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (68.340129ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.502359ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.08239ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.79788ms) +✔ no shim from this file's tests is left running for the after hook (25.157925ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (950.026134ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (456.763097ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (1018.61259ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3045.193125ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (749.608773ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (788.435946ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (711.377048ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5481.467648ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (981.028413ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (1027.723225ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (547.138591ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (649.637945ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (300.307963ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (459.679536ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3269.640619ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (41.334565ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (290.835409ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (335.082012ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (92.413809ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (411.927967ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (659.419553ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1559.002039ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (812.38225ms) +✔ 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) (465.632314ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (329.796416ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2807.774302ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (566.731504ms) +✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (604.989126ms) +✖ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (2542.372443ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2568.711852ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (280.507215ms) +✔ no shim from this file's tests is left running for the after hook (26.4886ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (209.488238ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (202.927672ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (200.23979ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (256.85389ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (197.146957ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.751775ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (188.9848ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (344.230194ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (380.320119ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (241.803609ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (271.365464ms) +✔ 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 (226.561464ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.142369ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (219.351216ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (221.08819ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (253.313493ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (205.011234ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.443345ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.271284ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.342211ms) +✔ 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.470868ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.35788ms) +✔ 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.532051ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.766349ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.428898ms) +✔ 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.356401ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.104975ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (244.665777ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (253.55239ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (291.595553ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (776.71445ms) +✔ H4: self-takeover is refused (326.945905ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (651.319089ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1081.309342ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (234.532032ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (549.6886ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (264.904697ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (182.954717ms) +✔ H13: a retry with the same request ID and different text is refused (241.729284ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (730.243349ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (297.716439ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (196.415793ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (848.242939ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (318.528706ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (732.465149ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (192.488473ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (339.805324ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.658929ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (677.30021ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (565.875447ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (740.833321ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3052.013796ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (633.102526ms) +✔ no shim from this file's tests is left running for the after hook (25.75198ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.875927ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.133952ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.96991ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.749885ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.53229ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.205763ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.947565ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.357601ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.501526ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.260406ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.696449ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.836895ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.693931ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.863366ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.15975ms) +✔ F9: registrations never add or redirect a root (1.678725ms) +✔ F10: a header cwd naming another project is refused (4.338571ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.880941ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.50669ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.085123ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.471323ms) +✔ F13: compaction is a marker in place, then the retained content (0.985429ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (543.976097ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.302138ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.706787ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.642896ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.358674ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.653337ms) +✔ every page and cursor is a valid CHAT-01 record (832.484232ms) +✔ the engine pin holds for the installed package (1.999531ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (276.297631ms) +✔ 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) (574.652583ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (269.138997ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (480.46456ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (408.407851ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (231.048426ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.130964ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (417.115509ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (403.354604ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (343.135342ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (544.160761ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1930.930805ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (200.844189ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (292.509154ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (305.910763ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (219.720988ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (381.512159ms) +✔ N10: fake conformance (30.857024ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (439.945628ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (286.871771ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (289.672433ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (356.781258ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (353.983378ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (340.074145ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (191.814421ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (365.451159ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (644.923613ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (468.295227ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (565.849602ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (233.962533ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (235.330515ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (230.600881ms) +✔ 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 (207.840992ms) +✔ 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 (210.222027ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 58995.641837 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1049:1 +✖ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (2542.372443ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [ + + [ + + 2472191, + + 'SIGKILL' + + ] + + ] + - [] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1065:12) + 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: [ [ 2472191, 'SIGKILL' ] ], + expected: [], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r2/mut/closeproof-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/closeproof-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/closeproof-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/manifest-after.txt b/agents/darkwing/work/queue-51-review/r2/mut/manifest-after.txt new file mode 100644 index 00000000..7adc1aca --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r2/mut/mutate.py b/agents/darkwing/work/queue-51-review/r2/mut/mutate.py new file mode 100644 index 00000000..f2d14728 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/mutate.py @@ -0,0 +1,35 @@ +# 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 = " // After every check, so a refused recover changes nothing (K6).\n await this.#releaseListed(\"recover\", this.claim);\n" +REL = " await this.#releaseListed(\"recover\", this.claim);\n" +CONF = " if (!this.#checkConfirmation(r, c, \"recover\")) return refused(\"confirmation\");\n" +TARGET = " if (session.leaf !== this.claim.record.leafAtProof || session.branch !== this.claim.record.branchAtProof) return refused(\"target\");\n" +KILL = " const kill = killEngine && this.b?.state !== \"stopped\";\n" +M = { + # Round 1's order: the release right after the stop-proof check. + "recconf": [(CT, CONF + RECREL, CONF), (CT, PROOF, PROOF + REL)], + # The release above the stop-proof check. + "recbefore": [(CT, CONF + RECREL, CONF), (CT, PROOF, REL + PROOF)], + # The release after the pins and target checks, before the confirmation. + "recpreconf": [(CT, CONF + RECREL, CONF), (CT, TARGET, TARGET + REL)], + # The release after the acquire. + "recafteracq": [(CT, CONF + RECREL, CONF), (CT, " const e = { id: newId(\"eligibility\")", REL + " const e = { id: newId(\"eligibility\")")], + "norecover": [(CT, CONF + RECREL, CONF)], + # The free-path release isn't awaited. + "startnoawait": [(CT, " await this.#releaseListed(\"start\", { claimId: h.record.claimId, record: h.record });\n", " void this.#releaseListed(\"start\", { claimId: h.record.claimId, record: h.record });\n")], + "noseat": [(CT, " for (const h of [prior, seatHead]) {\n", " for (const h of [prior]) {\n")], + "nolookup": [(CT, " if ((await this.units.lookup(claim.record)).state === \"absent\") return null;\n", "")], + "nodelete": [(CT, " if (this.scopeReleases.get(claim.claimId) === earlier) this.scopeReleases.delete(claim.claimId);\n", "")], + "closeproof": [(CT, KILL, " const kill = killEngine && !(this.b?.state === \"stopped\" && this.#stopped(this.stops.get(this.b.stop)));\n")], + "closekill": [(CT, KILL, " const kill = killEngine;\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/r2/mut/nodelete-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/nodelete-conversation.txt new file mode 100644 index 00000000..915f6024 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/nodelete-conversation.txt @@ -0,0 +1,217 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (226.896607ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (28.167646ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.533473ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (533.661328ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (573.639837ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (291.883319ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (297.703451ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (240.281394ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.639861ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12439.411312ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38250.348807ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (587.732298ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (142.627862ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (609.479298ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (278.793036ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (338.000199ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (107.101786ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (198.122106ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (260.187021ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (735.704419ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (183.858666ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (87.005575ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (66.908967ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.10703ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.080602ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.890131ms) +✔ no shim from this file's tests is left running for the after hook (24.462797ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1144.202948ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (623.245162ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (866.471947ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3157.241432ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (921.012814ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (866.497033ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (737.657858ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5680.305159ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (952.552614ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (899.213326ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (671.936336ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (656.353945ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (365.791973ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (463.193596ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3260.143648ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (23.751259ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (322.919993ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (260.296742ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (186.884461ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (567.983481ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (476.454332ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1111.571723ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (609.684895ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (469.888528ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (130.838241ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2959.690252ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (510.899033ms) +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (574.777454ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (608.135217ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2659.780102ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (297.423221ms) +✔ no shim from this file's tests is left running for the after hook (25.344182ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (207.208821ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (201.624311ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (312.677902ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (470.195358ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (275.342539ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.48336ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (295.018121ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (410.414633ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (222.041395ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (204.167426ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (202.253322ms) +✔ 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 (243.466736ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.007282ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (275.01146ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (235.870356ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (285.048607ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (301.444604ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.402355ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.249265ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.294525ms) +✔ 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.501275ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (29.99981ms) +✔ 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.441533ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.745967ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.386907ms) +✔ 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.337994ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.09934ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (226.63829ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (252.986371ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (322.748226ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1242.792072ms) +✔ H4: self-takeover is refused (278.57893ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (463.271877ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1152.415204ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (307.928665ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (862.829488ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (382.599142ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (328.329235ms) +✔ H13: a retry with the same request ID and different text is refused (279.84945ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (920.304786ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (329.976404ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (197.961154ms) +✔ 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.344803ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (346.300989ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (758.068719ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (196.489896ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (346.946876ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.646656ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (675.938599ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (532.404586ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (695.185473ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2945.789989ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (594.199638ms) +✔ no shim from this file's tests is left running for the after hook (25.672329ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.312514ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.717305ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.839754ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.548523ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.684725ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.027635ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.790368ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.428343ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.656609ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.990091ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.217317ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.156329ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.236428ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.871729ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.025644ms) +✔ F9: registrations never add or redirect a root (1.8747ms) +✔ F10: a header cwd naming another project is refused (3.909609ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.747161ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.683048ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (3.279355ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.805471ms) +✔ F13: compaction is a marker in place, then the retained content (0.830317ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (586.20837ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.247409ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.810645ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.498546ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.32926ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.189072ms) +✔ every page and cursor is a valid CHAT-01 record (828.456167ms) +✔ the engine pin holds for the installed package (1.881383ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (272.485936ms) +✔ 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) (611.46647ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (279.112616ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (501.209635ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (762.731897ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (327.419075ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.787925ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (520.917686ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (212.454075ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (299.890058ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (549.693022ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1946.173288ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (234.998239ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (461.304762ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (333.84256ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (360.655293ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (545.493017ms) +✔ N10: fake conformance (32.993837ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (417.593929ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (280.644408ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (302.592573ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (351.530989ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (350.226112ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (363.036491ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (202.782065ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (369.617713ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (656.570315ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (525.142557ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (498.476743ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (150.534811ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (192.210776ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (203.655826ms) +✔ 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 (229.867312ms) +✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (195.969388ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 59728.388899 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1004:1 +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (574.777454ms) + 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/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1030: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/r2/mut/nodelete-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/nodelete-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/nodelete-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/nolookup-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/nolookup-conversation.txt new file mode 100644 index 00000000..1f6f9d6d --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/nolookup-conversation.txt @@ -0,0 +1,217 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (209.812305ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (31.596713ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (16.865413ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (409.009847ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (382.734026ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (234.151608ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (198.261514ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (190.708869ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.948471ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11976.054483ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (39366.094305ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (262.918616ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (143.497175ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (616.11398ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (279.842443ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (359.418164ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (112.281746ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (215.496192ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (270.262984ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (754.103211ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (259.694804ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (91.528696ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (72.767099ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.52146ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.098365ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.791037ms) +✔ no shim from this file's tests is left running for the after hook (29.227788ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1035.074876ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (444.063051ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (747.970793ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2975.7536ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (679.355826ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (830.856437ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (833.005125ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5756.682073ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (768.039865ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (585.80089ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (659.24289ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (613.572007ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (315.870588ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (452.34197ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3285.257435ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (120.380693ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (381.635805ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (375.473852ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (86.767929ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (478.110743ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (656.340734ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1585.899954ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (847.578589ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (448.219879ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (318.640551ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (3026.507972ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (552.965142ms) +✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (704.598562ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (657.20104ms) +✖ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2541.711065ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (273.218951ms) +✔ no shim from this file's tests is left running for the after hook (24.675367ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (203.278494ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (190.159429ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (284.165594ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (276.216987ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (192.580108ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.427424ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (198.125481ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (306.1816ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (205.357906ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (171.935943ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (203.4889ms) +✔ 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 (190.720463ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.07304ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (193.427393ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (213.752328ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (234.238319ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (189.47783ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.396529ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.287241ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.317257ms) +✔ 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.471462ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.840346ms) +✔ 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.421114ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.788254ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.355126ms) +✔ 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.30335ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.100639ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (217.928978ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (244.055458ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (362.856651ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (800.119886ms) +✔ H4: self-takeover is refused (196.524786ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (411.032099ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (975.505451ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (212.751537ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (522.6803ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (259.129737ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (120.968325ms) +✔ H13: a retry with the same request ID and different text is refused (167.479335ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (602.934601ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (272.219954ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (200.97538ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (846.808199ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (445.990408ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (908.374606ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (197.114166ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (337.362618ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.675388ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (661.274751ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (567.551798ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (684.46252ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3069.254786ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (591.446983ms) +✔ no shim from this file's tests is left running for the after hook (25.600964ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.094545ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.67267ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.800012ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.399652ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.511162ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.792705ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.820786ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.175867ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.065015ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.002963ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.205844ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.321985ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.553294ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.248563ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.899766ms) +✔ F9: registrations never add or redirect a root (1.936515ms) +✔ F10: a header cwd naming another project is refused (4.081673ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.783219ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.2913ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.10679ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.463133ms) +✔ F13: compaction is a marker in place, then the retained content (0.784714ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (533.074989ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.832583ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.651953ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.36422ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.027547ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.904937ms) +✔ every page and cursor is a valid CHAT-01 record (755.259232ms) +✔ the engine pin holds for the installed package (1.789637ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (276.96992ms) +✔ 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) (519.896012ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (253.034583ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (542.506214ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (441.081989ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (229.238773ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.793548ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (351.907095ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (194.730682ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (277.342536ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (446.459741ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1861.831437ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (195.66958ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (280.564421ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (158.344467ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (174.885146ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (382.451067ms) +✔ N10: fake conformance (32.692746ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (412.148167ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (330.487566ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (334.684261ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (533.167021ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (344.461037ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (354.541594ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (160.860968ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (260.88606ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (699.367313ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (483.641113ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (444.86105ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (229.607426ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (174.032052ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (178.164937ms) +✔ 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 (188.268429ms) +✔ 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 (130.040069ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60228.738662 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1081:1 +✖ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2541.711065ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + + [ + + { + + at: '2026-10-10T06:55:17.157Z', + + claim: 'c28dd8cd48878438e098ae625', + + kind: 'release', + + outcome: 'unavailable', + + reason: 'the shim socket is gone', + + unitName: 'mosaic-chat-c28dd8cd48878438e098ae625', + + why: 'start' + + } + + ] + - [] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1097: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: [ { kind: 'release', claim: 'c28dd8cd48878438e098ae625', unitName: 'mosaic-chat-c28dd8cd48878438e098ae625', why: 'start', outcome: 'unavailable', reason: 'the shim socket is gone', at: '2026-10-10T06:55:17.157Z' } ], + expected: [], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r2/mut/nolookup-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/nolookup-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/nolookup-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/norecover-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/norecover-conversation.txt new file mode 100644 index 00000000..14a58ea8 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/norecover-conversation.txt @@ -0,0 +1,217 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (215.170552ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.477186ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (21.006625ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (364.840205ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (389.6812ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (239.41792ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (196.408249ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (197.043518ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.547323ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11891.313298ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (40420.670333ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (252.681062ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (139.685421ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (590.062676ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (291.664023ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (354.584775ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (117.772268ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (198.709689ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (293.169015ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (850.525028ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (320.677285ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (114.315183ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (69.629722ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.285896ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.114413ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.78544ms) +✔ no shim from this file's tests is left running for the after hook (25.527073ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1012.077119ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (443.019159ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (773.426661ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2973.269052ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (792.091835ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (832.297729ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (680.570445ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5644.26973ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (795.416915ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (688.579434ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (749.657801ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (771.86662ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (378.105552ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (491.332555ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3376.794173ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (25.283336ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (350.14358ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (316.621789ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.812787ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (408.532703ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (723.683599ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1450.673821ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (863.428173ms) +✔ 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.272796ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (319.898071ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2690.627541ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (497.20001ms) +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (445.507538ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (612.67698ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2746.456872ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (329.589344ms) +✔ no shim from this file's tests is left running for the after hook (29.296328ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (222.109682ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (209.503651ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (204.993231ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (270.92844ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (194.457793ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.459826ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (189.203179ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (292.403718ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (215.073906ms) +✔ 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.307285ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (198.545471ms) +✔ 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 (183.928173ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.948964ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (192.864984ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (208.927278ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (234.138913ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (194.908463ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.443642ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.271509ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.317933ms) +✔ 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.500376ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.007031ms) +✔ 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.499771ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.715733ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.366764ms) +✔ 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.334405ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.098534ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (218.196132ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (258.894123ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (291.911283ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (798.265657ms) +✔ H4: self-takeover is refused (195.719803ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (448.51721ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (973.316036ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (230.471509ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (523.538541ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (252.88333ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (121.08094ms) +✔ H13: a retry with the same request ID and different text is refused (203.838287ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (610.900181ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (284.492614ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (232.67284ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1109.159981ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (324.238266ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (831.653949ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (257.554531ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (511.50483ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.652272ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (655.745909ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (644.505046ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (700.44632ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2973.56528ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (667.673025ms) +✔ no shim from this file's tests is left running for the after hook (29.595585ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (8.87445ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.545328ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.780402ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.475793ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.503766ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.981315ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.782809ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.177764ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.107129ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.682489ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.769212ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.67859ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.648663ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.134571ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.815001ms) +✔ F9: registrations never add or redirect a root (1.740743ms) +✔ F10: a header cwd naming another project is refused (3.898653ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.646724ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.829802ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.81509ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.380724ms) +✔ F13: compaction is a marker in place, then the retained content (0.688858ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (499.657127ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.561411ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.426883ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.12751ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.013351ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.8574ms) +✔ every page and cursor is a valid CHAT-01 record (801.496918ms) +✔ the engine pin holds for the installed package (1.646285ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (250.79459ms) +✔ 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.688109ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (259.23397ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (504.737175ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (440.160182ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (221.91143ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.00951ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (351.9517ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (200.143859ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (297.338052ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (437.712847ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1880.007298ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (200.582058ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (291.272496ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (123.457006ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (222.823816ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (441.836439ms) +✔ N10: fake conformance (31.753601ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (558.888182ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (336.669293ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (293.04274ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (375.361684ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (406.465183ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (518.646315ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (265.696333ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (360.435017ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (694.070417ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (461.696881ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (526.185925ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (231.714244ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (199.196276ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (201.727211ms) +✔ 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 (206.304146ms) +✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (181.693035ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60796.862557 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1004:1 +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (445.507538ms) + 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/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1030: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/r2/mut/norecover-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/norecover-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/norecover-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/noseat-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/noseat-conversation.txt new file mode 100644 index 00000000..45c58d70 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/noseat-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (208.335245ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (30.804067ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.141178ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (449.2095ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (395.702205ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (314.793962ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (369.614912ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (282.170495ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.440145ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12139.771894ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38045.925585ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (271.299767ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (148.267221ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (707.182078ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (289.313634ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (356.117155ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (105.689856ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (199.658778ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (277.672583ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (854.170473ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (287.05972ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (133.594921ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (67.196345ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.108285ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.043674ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.720508ms) +✔ no shim from this file's tests is left running for the after hook (24.640047ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1048.808795ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (628.747876ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (865.840818ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3034.499324ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (744.83389ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (805.557532ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (687.314358ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5779.74429ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (804.639537ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (640.450825ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (659.791383ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (641.232446ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (305.297427ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (463.253744ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3247.804413ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (23.28651ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (493.243577ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (288.065011ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (61.270123ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (563.119537ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (496.541418ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1143.283558ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (676.033841ms) +✔ 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) (470.863571ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (149.113176ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2933.869625ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (600.549563ms) +✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (630.189503ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (434.438333ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2561.276153ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (293.926632ms) +✔ no shim from this file's tests is left running for the after hook (24.505904ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (198.925211ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (289.980101ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (199.771152ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (279.289306ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (187.287453ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.44446ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (332.782377ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (437.935405ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (277.693113ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (193.019713ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (230.4527ms) +✔ 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 (217.647024ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.870431ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (213.643309ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (219.64474ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (266.061614ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (203.893232ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.41743ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.253165ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.356142ms) +✔ 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.451286ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.787587ms) +✔ 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.436354ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.695302ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.393155ms) +✔ 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.341205ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.100036ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (219.226677ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (243.735089ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (381.377634ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1000.852196ms) +✔ H4: self-takeover is refused (311.157753ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (501.709605ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1091.831676ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (234.353692ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (532.66838ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (257.643221ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (248.448234ms) +✔ H13: a retry with the same request ID and different text is refused (257.956489ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (731.367457ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (277.171107ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (189.966526ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (861.355521ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (353.98858ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (741.917678ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (201.23048ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (324.71843ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.629585ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (783.649792ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (604.820208ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (721.411188ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3142.900143ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (613.31105ms) +✔ no shim from this file's tests is left running for the after hook (25.735249ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.506033ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.568698ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.771072ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.64893ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.582295ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.866454ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.778575ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.170755ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (2.964273ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.764444ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.073864ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.061467ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.089697ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.494513ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.838199ms) +✔ F9: registrations never add or redirect a root (1.734545ms) +✔ F10: a header cwd naming another project is refused (4.020293ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.91808ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.13723ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.987223ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.469848ms) +✔ F13: compaction is a marker in place, then the retained content (0.823311ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (513.288597ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.76624ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.563216ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.36197ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.091818ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.026722ms) +✔ every page and cursor is a valid CHAT-01 record (806.487462ms) +✔ the engine pin holds for the installed package (1.612443ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (280.251566ms) +✔ 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.292775ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (260.530626ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (557.019846ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (432.800671ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (228.799031ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.159821ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (647.110096ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (277.128403ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (297.290958ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (512.775306ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1950.550684ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (318.848428ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (363.353454ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (180.594145ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (209.599186ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (380.233193ms) +✔ N10: fake conformance (32.898433ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (430.218948ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (275.519677ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (296.148196ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (354.434361ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (347.797905ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (355.902735ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (185.30156ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (520.539816ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (824.293104ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (531.747838ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (452.349178ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (190.09251ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (222.999335ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (237.821861ms) +✔ 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.32801ms) +✔ 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 (176.575088ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 59122.611429 diff --git a/agents/darkwing/work/queue-51-review/r2/mut/noseat-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/noseat-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/noseat-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/recafteracq-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/recafteracq-conversation.txt new file mode 100644 index 00000000..2df2b611 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/recafteracq-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (224.276041ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (34.245092ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (22.236075ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (580.596071ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (405.237972ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (252.194002ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (239.508739ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (212.686409ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.473714ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11899.291831ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (37048.584383ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (257.187704ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (142.610484ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (629.479348ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (278.736789ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (344.772895ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (107.630612ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (198.095481ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (272.512172ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (722.993264ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (176.47258ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (84.673881ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (72.809741ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.197943ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.083562ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.813762ms) +✔ no shim from this file's tests is left running for the after hook (23.921731ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1148.142792ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (514.348128ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (635.841017ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2853.131257ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (528.072967ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (603.463883ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (560.347123ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5403.131079ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (849.098362ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (708.729863ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (545.872657ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (803.989196ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (448.335724ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (689.38692ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3310.752385ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (41.265728ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (448.247413ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (400.737811ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.411625ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (405.951543ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (581.650142ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1208.977412ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (745.07535ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (469.029856ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (128.249475ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2713.997118ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (879.864057ms) +✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (835.068065ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (550.007806ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (3104.39794ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (358.699967ms) +✔ no shim from this file's tests is left running for the after hook (33.368854ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (217.756576ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (345.390643ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (240.212003ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (308.736158ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (220.848815ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.432648ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (218.376943ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (340.680597ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (239.172097ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (214.522511ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (203.844477ms) +✔ 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 (226.859903ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (9.922895ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (230.877469ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (256.914076ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (470.691858ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (200.849178ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.401129ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.253758ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.30143ms) +✔ 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.51301ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (29.97913ms) +✔ 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.450825ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.695976ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.363883ms) +✔ 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.32382ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.100439ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (224.245948ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (259.126547ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (440.230376ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (954.678392ms) +✔ H4: self-takeover is refused (223.363243ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (481.276316ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1371.800544ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (210.261459ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (544.597895ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (339.477899ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (195.753394ms) +✔ H13: a retry with the same request ID and different text is refused (238.363702ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (602.7895ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (287.773078ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (210.064333ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (916.804377ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (341.950362ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (779.012529ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (288.631243ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (323.266ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.667803ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (681.134503ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (569.855993ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (790.489911ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3066.603452ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (676.840062ms) +✔ no shim from this file's tests is left running for the after hook (26.684167ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.029149ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.857382ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.803048ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.418186ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.439927ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.064654ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.84148ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.220585ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.930635ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.960169ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.334059ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.43036ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.423236ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.761852ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.739403ms) +✔ F9: registrations never add or redirect a root (1.680381ms) +✔ F10: a header cwd naming another project is refused (4.749424ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.835039ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.786641ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.360068ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.45859ms) +✔ F13: compaction is a marker in place, then the retained content (0.878861ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (509.534207ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.51014ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.431615ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.226766ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.039357ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.855545ms) +✔ every page and cursor is a valid CHAT-01 record (750.776831ms) +✔ the engine pin holds for the installed package (1.781262ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (268.468798ms) +✔ 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) (563.474712ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (242.011791ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (641.21541ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (507.011541ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (255.049849ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.836272ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (410.713002ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (226.21061ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (318.216617ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (501.077015ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1900.513065ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (194.941654ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (339.17847ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (193.058971ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (237.298597ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (366.033663ms) +✔ N10: fake conformance (33.065318ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (426.501011ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (312.073069ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (303.988614ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (357.840398ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (348.838366ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (369.560143ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (210.512832ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (458.452105ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (757.178809ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (570.315461ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (472.512345ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (334.656683ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (208.14998ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (252.247066ms) +✔ 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 (238.775426ms) +✔ 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 (237.999996ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 57450.555787 diff --git a/agents/darkwing/work/queue-51-review/r2/mut/recafteracq-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/recafteracq-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/recafteracq-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/recbefore-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/recbefore-conversation.txt new file mode 100644 index 00000000..7e2b4ac8 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/recbefore-conversation.txt @@ -0,0 +1,215 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (211.958885ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (29.960168ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (16.341998ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (470.503578ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (421.52755ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (250.22264ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (202.939545ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (343.035588ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.520463ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11604.979262ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (36539.259777ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (292.776851ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (145.958948ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (627.886066ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (271.42409ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (409.861616ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (107.011865ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (229.246448ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (264.111111ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (742.655551ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (188.388325ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (98.982846ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (74.824269ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.416892ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.099809ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.689025ms) +✔ no shim from this file's tests is left running for the after hook (24.265662ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (897.511956ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (501.187702ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (721.7731ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2792.24843ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (516.101412ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (696.405054ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (790.540644ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5551.786173ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (737.124835ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (573.621717ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (570.405763ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (864.599047ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (511.116161ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (446.215128ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3284.684864ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (23.776247ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (383.89008ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (400.14541ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.911807ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (441.131681ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (512.330227ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1208.484694ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (1018.165239ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (468.130204ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (170.978572ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2834.549352ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (480.819663ms) +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (412.719114ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (437.657747ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2852.054048ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (395.673441ms) +✔ no shim from this file's tests is left running for the after hook (24.189207ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (202.660235ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (215.367686ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (304.704976ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (299.338767ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (221.245526ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.427986ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (216.895719ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (314.71448ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (325.086317ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (279.66165ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (207.524721ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (201.015013ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.927189ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (207.551642ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (215.322145ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (244.593291ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (194.363396ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.42249ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.262042ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.324137ms) +✔ 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.464316ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.948215ms) +✔ 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.780661ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.694361ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.326778ms) +✔ 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.334249ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.113653ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (224.501774ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (256.275432ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (411.124909ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (869.944763ms) +✔ H4: self-takeover is refused (309.229816ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (479.055933ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1011.289537ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (165.178179ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (502.134199ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (277.805647ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (197.013505ms) +✔ H13: a retry with the same request ID and different text is refused (211.094725ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (596.697907ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (344.232562ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (246.027616ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (975.38381ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (345.29645ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (792.007861ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (206.897442ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (339.479585ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.594249ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (658.635923ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (740.560857ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (832.500122ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3001.000123ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (614.966714ms) +✔ no shim from this file's tests is left running for the after hook (24.70864ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.958501ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.918143ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.98309ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.683498ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.48295ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.166673ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.877164ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.564766ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.713257ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.522341ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.579185ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.494397ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (8.042311ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.207736ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.849865ms) +✔ F9: registrations never add or redirect a root (1.732921ms) +✔ F10: a header cwd naming another project is refused (3.971515ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.782576ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.241832ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.06562ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.435508ms) +✔ F13: compaction is a marker in place, then the retained content (0.786967ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (513.773782ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.408661ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.419166ms) +✔ unknown conversations, empty files and non-Pi files refuse (3.027247ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.123539ms) +✔ a seat directory without search permission refuses that root, not the catalogue (1.914753ms) +✔ every page and cursor is a valid CHAT-01 record (815.714898ms) +✔ the engine pin holds for the installed package (2.064865ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (256.205333ms) +✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (513.054807ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (283.652637ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (491.970658ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (567.539955ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (256.650578ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.036306ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (378.333597ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (263.207522ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (391.436359ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (484.878691ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1897.078987ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (212.246785ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (293.029579ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (179.734968ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (272.894268ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (502.516027ms) +✔ N10: fake conformance (33.851012ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (537.830687ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (269.882505ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (286.309944ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (373.573718ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (370.244517ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (375.875036ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (206.884823ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (327.887838ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (756.308683ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (597.225894ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (666.165563ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (296.347293ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (206.398192ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (211.325724ms) +✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (208.867155ms) +✔ 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 (150.939072ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 57236.029796 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1004:1 +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (412.719114ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + [ + [ + 'force-stop', + 'unavailable' + ], + + [ + + 'recover', + + 'released' + + ] + ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1026: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' ], [ 'recover', 'released' ] ], + expected: [ [ 'force-stop', 'unavailable' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r2/mut/recbefore-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/recbefore-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/recbefore-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/recconf-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/recconf-conversation.txt new file mode 100644 index 00000000..f594e8bd --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/recconf-conversation.txt @@ -0,0 +1,215 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (264.947409ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (35.35371ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (24.6948ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (418.158589ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (454.532882ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (277.08111ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (230.285948ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (226.476336ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.720464ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12714.970145ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38014.603994ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (272.431381ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (175.601545ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (808.081182ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (277.717109ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (348.822811ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (118.390096ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (202.342613ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (366.330975ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1113.061177ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (266.746299ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (107.785347ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (70.992438ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.411726ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.057437ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.743979ms) +✔ no shim from this file's tests is left running for the after hook (25.713161ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1041.793627ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (551.298708ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (881.161215ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3149.961144ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (744.198249ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (917.869588ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (777.111469ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (6136.868929ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (812.234851ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (666.648443ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (721.783192ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (556.028097ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (335.361745ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (482.20854ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3236.675895ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (35.492232ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (350.775572ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (269.293855ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (112.425861ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (348.228132ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (562.240019ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1435.712925ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (684.958149ms) +✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (469.333903ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (150.983736ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2684.697769ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (548.809239ms) +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (553.41641ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (450.879837ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2622.681766ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (278.78895ms) +✔ no shim from this file's tests is left running for the after hook (22.792533ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (267.902207ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (237.561224ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (237.375866ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (339.023306ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (264.345834ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.435006ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (229.201308ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (332.507301ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (259.31901ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (225.06733ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (238.820482ms) +✔ 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 (296.641394ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.659768ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (237.880281ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (241.43922ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (290.242591ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (255.615096ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.395397ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.257531ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.314626ms) +✔ 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.520481ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.052516ms) +✔ 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.4807ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.753935ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.377655ms) +✔ 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.843527ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.100207ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (235.442879ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (316.40165ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (335.777717ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (980.59686ms) +✔ H4: self-takeover is refused (229.517161ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (525.007908ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1266.035771ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (297.19431ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (753.362826ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (402.345554ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (226.688158ms) +✔ H13: a retry with the same request ID and different text is refused (195.250413ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (672.578267ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (414.313965ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (226.399649ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (976.839815ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (396.142538ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1121.577209ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (215.215483ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (354.865079ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.645369ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (680.410159ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (553.444648ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (728.306273ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2969.173218ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (631.503385ms) +✔ no shim from this file's tests is left running for the after hook (30.099439ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.510772ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.978582ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.178874ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.864086ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.940529ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.491035ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.953011ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.435892ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.888682ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.368458ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.052692ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (6.587632ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (8.242846ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.782678ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (3.040379ms) +✔ F9: registrations never add or redirect a root (2.773731ms) +✔ F10: a header cwd naming another project is refused (7.045157ms) +✔ F11: parentSession renders with a marker and the parent is never opened (1.223336ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (7.496866ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.338215ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.69831ms) +✔ F13: compaction is a marker in place, then the retained content (0.874033ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (543.549405ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.240004ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.800149ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.790933ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.40637ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.364965ms) +✔ every page and cursor is a valid CHAT-01 record (803.379266ms) +✔ the engine pin holds for the installed package (2.023074ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (310.42466ms) +✔ 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.403331ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (263.283747ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (600.671629ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (516.818756ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (307.571899ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (23.03312ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (405.282364ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (240.433892ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (326.502964ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (592.499206ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1959.275569ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (207.731374ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (313.18067ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (219.051601ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (266.042742ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (454.039531ms) +✔ N10: fake conformance (31.7504ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (552.99864ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (325.882114ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (309.906978ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (404.305145ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (404.087587ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (415.562475ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (481.404826ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (389.779315ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (721.199238ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (576.637358ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (494.071685ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (263.068864ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (222.416903ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (221.813038ms) +✔ 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 (220.529956ms) +✔ 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.268215ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60509.784615 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1004:1 +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (553.41641ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + [ + [ + 'force-stop', + 'unavailable' + ], + + [ + + 'recover', + + 'released' + + ] + ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1026: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' ], [ 'recover', 'released' ] ], + expected: [ [ 'force-stop', 'unavailable' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r2/mut/recconf-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/recconf-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/recconf-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/recpreconf-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/recpreconf-conversation.txt new file mode 100644 index 00000000..38378b57 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/recpreconf-conversation.txt @@ -0,0 +1,215 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (297.104214ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (42.102492ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (42.849545ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (459.503427ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (438.278885ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (254.053586ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (207.844531ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (197.620933ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.513686ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12197.524399ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (36892.685455ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (320.262508ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (164.961047ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (771.896626ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (357.562692ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (504.788946ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (176.59688ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (265.915761ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (480.422974ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (969.567699ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (189.539013ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (96.900449ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (71.45642ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.530222ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.135087ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.836776ms) +✔ no shim from this file's tests is left running for the after hook (24.268599ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (874.141267ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (548.94794ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (598.428892ms) +✔ K4: two engines; force stop one; the other survives by independent observation (2874.287719ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (861.71016ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (860.557523ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (662.361482ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5618.249577ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (855.192038ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (601.999032ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (426.255576ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (660.089301ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (471.07349ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (483.127129ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3263.341924ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (24.771603ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (393.351266ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (505.087401ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (245.589835ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (686.297616ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (1201.843304ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1562.067891ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (853.617944ms) +✔ 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) (479.186502ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (376.594148ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (3215.254254ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (653.440932ms) +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (551.848702ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (717.541607ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2647.777733ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (405.287027ms) +✔ no shim from this file's tests is left running for the after hook (25.577974ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (336.247858ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (316.700648ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (269.660953ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (327.897507ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (226.42036ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.426849ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (210.654385ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (292.018923ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (247.549412ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (240.605619ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (214.271049ms) +✔ 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 (217.38884ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.007639ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (202.01105ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (228.551614ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (237.401404ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (193.087323ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.428087ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.28537ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.312901ms) +✔ 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.500472ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.133186ms) +✔ 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.478062ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.869248ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.450064ms) +✔ 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.341713ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.094632ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (233.00939ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (403.707857ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (388.165837ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (919.399543ms) +✔ H4: self-takeover is refused (186.257973ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (525.822379ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1036.147439ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (216.662448ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (538.949552ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (264.12417ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (131.303734ms) +✔ H13: a retry with the same request ID and different text is refused (193.985449ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (552.645989ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (289.004961ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (206.418067ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (873.560085ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (348.61363ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (754.390638ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (175.417089ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (340.575023ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.626584ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (656.489649ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (554.33542ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (718.596812ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2972.129836ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (674.785368ms) +✔ no shim from this file's tests is left running for the after hook (25.914158ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.572048ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.530231ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.773932ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.459041ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.331129ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.889302ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.772245ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.233077ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.518698ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.333228ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.404933ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.30556ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.172086ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.237802ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.640178ms) +✔ F9: registrations never add or redirect a root (1.526778ms) +✔ F10: a header cwd naming another project is refused (3.447216ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.650893ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.707053ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.770173ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.2625ms) +✔ F13: compaction is a marker in place, then the retained content (0.680814ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (498.497901ms) +✔ fragments never cut a surrogate pair and keep an empty string (5.047347ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.55498ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.51719ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.169498ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.069809ms) +✔ every page and cursor is a valid CHAT-01 record (770.127883ms) +✔ the engine pin holds for the installed package (1.662621ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (254.928902ms) +✔ 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) (500.49113ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (258.38522ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (735.777761ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (540.606826ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (263.539491ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.875909ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (353.51312ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (236.952565ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (330.649176ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (485.81671ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1878.032569ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (191.195644ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (281.821233ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (193.489813ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (195.979094ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (409.762782ms) +✔ N10: fake conformance (33.397479ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (438.783154ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (258.729852ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (289.817062ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (364.018275ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (359.301475ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (330.080207ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (198.06304ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (320.467282ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (650.492454ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (419.824533ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (505.604124ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (216.791965ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (202.548113ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (200.322565ms) +✔ 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 (194.158269ms) +✔ 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 (178.497429ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 58795.353516 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:1004:1 +✖ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (551.848702ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: + + actual - expected + + [ + [ + 'force-stop', + 'unavailable' + ], + + [ + + 'recover', + + 'released' + + ] + ] + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51b/wt2/packages/conversation/tests/cohort.test.mjs:1026: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' ], [ 'recover', 'released' ] ], + expected: [ [ 'force-stop', 'unavailable' ] ], + operator: 'deepStrictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r2/mut/recpreconf-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/recpreconf-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/recpreconf-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/run.sh b/agents/darkwing/work/queue-51-review/r2/mut/run.sh new file mode 100755 index 00000000..19f57ab2 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r51b/muttmp/mut-$m DOCKER_HOST=unix:///nonexistent.sock +mkdir -p $TMPDIR +M=~/darkwing-scratch/r51b/mut; cd ~/darkwing-scratch/r51b/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/r2/mut/startnoawait-conversation.txt b/agents/darkwing/work/queue-51-review/r2/mut/startnoawait-conversation.txt new file mode 100644 index 00000000..94d426b2 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/startnoawait-conversation.txt @@ -0,0 +1,205 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (232.354988ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.278803ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (21.181463ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (561.49758ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (494.807754ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (311.283881ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (311.925349ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (221.512723ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.366163ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12013.651124ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38464.652826ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (259.940021ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (139.408344ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (643.731884ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (275.778957ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (332.632966ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (105.719633ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (190.270568ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (257.564243ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (720.413493ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (188.23406ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (97.032994ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (69.066726ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.081063ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.099107ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.752774ms) +✔ no shim from this file's tests is left running for the after hook (24.100311ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1124.004421ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (650.104063ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (827.067014ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3019.167282ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (691.240889ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (811.093133ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (711.138089ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5664.000003ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (993.803345ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (826.174909ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (654.418889ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (652.963495ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (317.754726ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (501.733936ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3258.053308ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (25.786224ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (323.312577ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (296.236361ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.806022ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (480.646861ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (725.283719ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1161.667499ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (655.227568ms) +✔ 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.094989ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (374.913543ms) +✖ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2633.661737ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (799.858285ms) +✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (592.086319ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (398.216694ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2775.957761ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (397.616211ms) +✔ no shim from this file's tests is left running for the after hook (24.618707ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (223.113072ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (402.385907ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (200.91629ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (352.545712ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (386.193275ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.524483ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (204.459707ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (434.790765ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (208.628416ms) +✔ 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.207965ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (209.525324ms) +✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (214.791001ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.257555ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (207.785389ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (222.93139ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (246.012191ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (217.850966ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.378358ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.255604ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.315044ms) +✔ 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.482176ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.79974ms) +✔ 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.456685ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.694544ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.358734ms) +✔ 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.31609ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.100786ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (227.856584ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (277.283003ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (495.610302ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1136.441089ms) +✔ H4: self-takeover is refused (266.830013ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (420.918645ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1028.026142ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (229.392447ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (640.957893ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (295.422428ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (244.62704ms) +✔ H13: a retry with the same request ID and different text is refused (212.181907ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (601.446306ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (308.514796ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (210.690231ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (866.919372ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (351.68384ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (721.987863ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (185.048847ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (327.914413ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.684845ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (671.906917ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (557.834039ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (673.114767ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3121.386021ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (715.134803ms) +✔ no shim from this file's tests is left running for the after hook (27.256085ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (9.312309ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.719201ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (3.197326ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (4.243174ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.602719ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.064589ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.856939ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.297126ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.816202ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.470416ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.99739ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.641481ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.965942ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.361429ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (1.888174ms) +✔ F9: registrations never add or redirect a root (1.790481ms) +✔ F10: a header cwd naming another project is refused (4.222416ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.766335ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.392649ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.122902ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.57253ms) +✔ F13: compaction is a marker in place, then the retained content (0.802449ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (522.209998ms) +✔ fragments never cut a surrogate pair and keep an empty string (4.524039ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (16.690237ms) +✔ unknown conversations, empty files and non-Pi files refuse (2.052498ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (0.988824ms) +✔ a seat directory without search permission refuses that root, not the catalogue (4.230078ms) +✔ every page and cursor is a valid CHAT-01 record (793.552903ms) +✔ the engine pin holds for the installed package (1.805911ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (287.764811ms) +✔ 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) (608.252102ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (259.958004ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (714.851606ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (516.228968ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (424.768279ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.946522ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (492.321296ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (196.243286ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (274.687332ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (479.450814ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1930.182747ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (219.832833ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (303.065429ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (208.542039ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (196.17292ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (390.615086ms) +✔ N10: fake conformance (31.997685ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (439.054261ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (273.274049ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (281.710039ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (361.013334ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (338.674289ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (329.743881ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (185.670772ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (303.936609ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (654.783311ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (484.74142ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (397.040995ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (175.364992ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (140.122467ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (133.075267ms) +✔ 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 (132.032987ms) +✔ 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 (166.035429ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 176 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 59120.208858 + +✖ 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 (2633.661737ms) + AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: + + 0 !== 1 + + at TestContext. (file:///home/jwoltje/darkwing-scratch/r51b/wt2/packages/conversation/tests/cohort.test.mjs:991:28) + 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: 0, + expected: 1, + operator: 'strictEqual', + diff: 'simple' + } diff --git a/agents/darkwing/work/queue-51-review/r2/mut/startnoawait-shims-left.txt b/agents/darkwing/work/queue-51-review/r2/mut/startnoawait-shims-left.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/startnoawait-shims-left.txt @@ -0,0 +1 @@ + diff --git a/agents/darkwing/work/queue-51-review/r2/mut/summary.txt b/agents/darkwing/work/queue-51-review/r2/mut/summary.txt new file mode 100644 index 00000000..92b17270 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/mut/summary.txt @@ -0,0 +1,13 @@ +start 2026-10-10T06:47:51Z +recconf rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +recbefore rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +recpreconf rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +recafteracq rc 0 ℹ pass 177 ℹ fail 0 shims-left 0 +norecover rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +startnoawait rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +noseat rc 0 ℹ pass 177 ℹ fail 0 shims-left 0 +nolookup rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +nodelete rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +closeproof rc 1 ℹ pass 176 ℹ fail 1 shims-left 0 +closekill rc 1 ℹ pass 175 ℹ fail 2 shims-left 0 +end 2026-10-10T06:58:43Z diff --git a/agents/darkwing/work/queue-51-review/r2/out/node-control-board.txt b/agents/darkwing/work/queue-51-review/r2/out/node-control-board.txt new file mode 100644 index 00000000..b8b0bf21 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/out/node-control-board.txt @@ -0,0 +1,132 @@ +✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (3.720805ms) +✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.231591ms) +✔ completed smoke replies and ordinary questions are idle, not human blockers (0.756859ms) +✔ only an explicit first-line input request makes a finished reply waiting (0.155247ms) +✔ tool activity, user text, errors and unfinished turns override attention text (0.129266ms) +✔ STOP access failure is unknown, not absence, under a non-root identity (34.957147ms) +✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (2.850481ms) +✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.437301ms) +✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (5.718902ms) +✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.55417ms) +✔ server rescans connector discovery and refuses HTTP reply without transport (34.831061ms) +✔ connector session links and linked directories are not read (1.140204ms) +✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (2.284298ms) +✔ CLI print uses the relaunch notice instead of old current preview (56.579498ms) +✔ connector owner and fixed task never inherit a native relaunch notice (2.132768ms) +✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.211392ms) +✔ loadConfig: missing file throws ConfigError (1.409816ms) +✔ loadConfig: invalid JSON throws ConfigError (0.340941ms) +✔ loadConfig: missing dataRoot throws ConfigError (0.210714ms) +✔ loadConfig: relative dataRoot throws ConfigError (0.216754ms) +✔ loadConfig: valid config returns dataRoot (0.595039ms) +✔ findNewestSession: picks the newest by mtime among two files (0.418702ms) +✔ findNewestSession: finds files in nested subdirectories (0.302951ms) +✔ findNewestSession: returns null for a missing dir (0.150042ms) +✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.595572ms) +✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.154061ms) +✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.437752ms) +✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.037412ms) +✔ deriveState: full state table (0.166197ms) +✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.286196ms) +✔ rule: newest entry is a tool result with no assistant text after it is working (0.252317ms) +✔ rule: a finished ordinary turn is idle, even if it says your move (0.243231ms) +✔ task: the first user message of the session, from text blocks (0.236427ms) +✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.248753ms) +✔ task: no user message in the log means null (shown as unknown), never a guess (0.2404ms) +✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.340106ms) +✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.516621ms) +✔ scan: the written record carries task, workspace and activeProject (0.451829ms) +✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.527779ms) +✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.350807ms) +✔ registration: a record whose pid is gone is stale; derived values win, sources say derived, registered stays with alive false; a pid the probe cannot decide is not stale; pidAlive itself (0.895535ms) +✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.381734ms) +✔ loadRegistrations: a missing seatsDir gives empty lists (0.127087ms) +✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.666671ms) +✔ 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.296397ms) +✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (1.083156ms) +✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.214634ms) +✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.430318ms) +✔ scanAgent: ageSeconds is computed from the injected now (0.225173ms) +✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.1349ms) +✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.303093ms) +✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.343605ms) +✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.800285ms) +✔ scan: relative boardDir throws ConfigError (0.082516ms) +✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (55.91655ms) +✔ CLI: missing config exits 2 with a refused: message (54.578639ms) +✔ CLI: unknown command exits 2 (50.636832ms) +✔ CLI: unknown --liveness value exits 2 (55.990862ms) +✔ panesRunPi: true when any trimmed line equals 'pi' (0.242325ms) +✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.092365ms) +✔ tmuxIsAlive: a pane running pi is alive (0.217599ms) +✔ tmuxIsAlive: session exists but pi has exited is not alive (0.078076ms) +✔ tmuxIsAlive: no such tmux session is not alive (0.069604ms) +✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.069513ms) +✔ tmuxIsAlive: passes -L only when a socket is given (0.105914ms) +✔ parsePanes: one pane per line, command and optional tab-separated path (0.087208ms) +✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.085758ms) +✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.105291ms) +✔ loadSeen: missing file returns {} (0.168507ms) +✔ loadSeen: invalid JSON throws ConfigError (0.215272ms) +✔ loadSeen: a JSON array throws ConfigError (0.170824ms) +✔ loadSeen: a non-string value throws ConfigError (0.189685ms) +✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.379032ms) +✔ markSeen: seen false deletes the key (0.31735ms) +✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.238033ms) +✔ markSeen: project containing '/' throws ConfigError (0.204025ms) +✔ markSeen: non-boolean seen throws ConfigError (0.1952ms) +✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.343328ms) +✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.335678ms) +✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.277196ms) +✔ scanAgent: an error-state session with a matching mark is seen (0.256877ms) +✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.570268ms) +✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.22088ms) +✔ 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.766968ms) +✔ 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.651403ms) +✔ 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 (1.027169ms) +✔ isLoopbackHost: recognizes loopback hosts (1.165522ms) +✔ isLoopbackHost: rejects non-loopback hosts (4.169004ms) +✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.335733ms) +✔ startServer: serves page, healthz, and a rescanning /api/board (35.492884ms) +✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (6.456487ms) +✔ startServer: /api/board returns 500 JSON with an error field when scan throws (2.594768ms) +✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (56.167392ms) +✔ CLI: serve rejects a non-numeric --port with exit 2 (50.78742ms) +✔ CLI: scan still works after the async cli refactor (50.803175ms) +✔ CLI: live serve prints its URL and answers /healthz (63.281177ms) +✔ page.html: esc() escapes every HTML-significant character (0.602624ms) +✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (9.008453ms) +✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (1.987018ms) +✔ POST /api/seen with invalid JSON returns 400 (2.800995ms) +✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (2.083993ms) +✔ POST /api/seen with a missing agent returns 400 (1.315879ms) +✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.79913ms) +✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (51.939432ms) +✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.311658ms) +✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.268297ms) +✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.15774ms) +✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.129566ms) +✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.697448ms) +✔ 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.562308ms) +✔ 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.714185ms) +✔ POST /api/reply: a registration with a tmux socket adds -L (27.055093ms) +✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (36.335961ms) +✔ 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.905579ms) +✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.72918ms) +✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.16806ms) +✔ 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.579465ms) +✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.486587ms) +✔ 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.300772ms) +✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (8.147614ms) +✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (34.258693ms) +✔ 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.36354ms) +✔ every refusal code the reader can raise has an HTTP status (0.914577ms) +✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (46.117604ms) +ℹ tests 124 +ℹ suites 0 +ℹ pass 124 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 642.951021 diff --git a/agents/darkwing/work/queue-51-review/r2/out/node-conversation.txt b/agents/darkwing/work/queue-51-review/r2/out/node-conversation.txt new file mode 100644 index 00000000..179155e8 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/out/node-conversation.txt @@ -0,0 +1,185 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (597.429003ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (40.954663ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (22.786834ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (436.808045ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (454.299254ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (263.424205ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (272.125077ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (280.860876ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.321896ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (15442.268306ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38878.226775ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (308.230077ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (179.569827ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (789.45693ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (306.446157ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (359.243563ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (143.555489ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (216.770471ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (261.528248ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (732.424205ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (184.306631ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (92.396695ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (71.719283ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.188625ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.050245ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.766601ms) +✔ no shim from this file's tests is left running for the after hook (24.382928ms) +✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (1258.500505ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (574.76375ms) +✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (1089.622732ms) +✔ K4: two engines; force stop one; the other survives by independent observation (3335.415607ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (898.301693ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (933.17654ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (744.388173ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (6012.188982ms) +✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (1094.887185ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (928.160229ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (907.561325ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (1844.132732ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (800.065323ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (970.579212ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3511.266329ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (47.870479ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (612.731605ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (565.51884ms) +✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.890468ms) +✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (614.675809ms) +✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (772.72612ms) +✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1771.565041ms) +✔ R4: after a normal engine exit the scope stays until a proven force stop releases it (905.141171ms) +✔ 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) (488.1033ms) +✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (169.830008ms) +✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2940.654947ms) +✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (653.83933ms) +✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (741.044451ms) +✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (494.799094ms) +✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2707.140013ms) +✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (290.262258ms) +✔ no shim from this file's tests is left running for the after hook (24.778786ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (622.128704ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (300.825282ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (240.561873ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (372.93656ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (252.201002ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.711935ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (271.788979ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (376.507665ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (600.067881ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (380.539687ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (270.431244ms) +✔ 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 (306.027417ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.561473ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (265.91842ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (287.513903ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (315.357808ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (262.980595ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.397905ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.260694ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.335573ms) +✔ 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.495242ms) +✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.973599ms) +✔ 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.421448ms) +✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.660811ms) +✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.338748ms) +✔ 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.32873ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.10422ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (230.400298ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (653.150176ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (403.187068ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1062.773413ms) +✔ H4: self-takeover is refused (323.883638ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (1001.823003ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1397.568741ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (301.405362ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1003.344709ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (500.407872ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (219.149076ms) +✔ H13: a retry with the same request ID and different text is refused (212.761321ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (685.726227ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (311.346956ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (219.885369ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (979.029191ms) +✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (420.895881ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1575.278781ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (267.216566ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (503.892121ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.672585ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (809.520784ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (710.867603ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (1046.420902ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3203.72305ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (774.698692ms) +✔ no shim from this file's tests is left running for the after hook (25.168567ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.277121ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.76589ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.710685ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.405706ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.369735ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.998627ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (0.849718ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.462077ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.875461ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.946285ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.219958ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.330458ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.497358ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (10.539458ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.849015ms) +✔ F9: registrations never add or redirect a root (2.003292ms) +✔ F10: a header cwd naming another project is refused (5.719428ms) +✔ F11: parentSession renders with a marker and the parent is never opened (1.161772ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (7.044442ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (3.383635ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (2.382306ms) +✔ F13: compaction is a marker in place, then the retained content (1.223236ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (556.637487ms) +✔ fragments never cut a surrogate pair and keep an empty string (7.107353ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (2.317922ms) +✔ unknown conversations, empty files and non-Pi files refuse (3.784049ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.191234ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.16053ms) +✔ every page and cursor is a valid CHAT-01 record (826.794516ms) +✔ the engine pin holds for the installed package (2.204289ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (275.749316ms) +✔ 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) (592.272344ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (267.154931ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (1007.851326ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (570.541339ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (299.49222ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.98398ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (496.710918ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (600.387309ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (465.448988ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (659.831336ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2041.813279ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (363.61738ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (286.892274ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (214.649233ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (266.662438ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (510.244859ms) +✔ N10: fake conformance (34.05928ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (420.27127ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (288.353793ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (343.93325ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (429.948175ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (431.01797ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (739.636959ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (540.57363ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (479.358507ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (1180.289784ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (806.745071ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (959.642257ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (301.877165ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (262.131945ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (272.31997ms) +✔ 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 (312.121732ms) +✔ 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 (307.878355ms) +ℹ tests 177 +ℹ suites 0 +ℹ pass 177 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 63911.226669 diff --git a/agents/darkwing/work/queue-51-review/r2/out/node-queue.txt b/agents/darkwing/work/queue-51-review/r2/out/node-queue.txt new file mode 100644 index 00000000..42f55c2b --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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 (1405.027247ms) +✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1256.223427ms) +ℹ 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 (1364.988847ms) +ℹ 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 (1253.192262ms) +✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1140.688659ms) +✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1179.218113ms) +✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1520.834607ms) +✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1208.393201ms) +✔ F1: a shared-index change during the procedure is not committed (1349.817188ms) +✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1233.269115ms) +✔ F1: a missing or a different hook refuses (893.363806ms) +✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1512.378613ms) +✔ F1: the canary refuses a hook that git would not run (846.626261ms) +✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1271.772154ms) +✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (1154.763397ms) +✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (935.746574ms) +✔ bootstrap: the archived tests and validator run outside any repository (1052.392278ms) +✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (973.871615ms) +✔ general: a queue write after the snapshot is not committed (1344.426355ms) +✔ general: a snapshot whose log does not extend the base refuses (1036.874077ms) +✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (163.692047ms) +✔ general: environment overrides, a linked worktree and usage (677.029562ms) +✔ general: a queue path staged before the run refuses at step 1 (703.413217ms) +✔ general: HEAD's queue tests failing in the archive refuse (929.321661ms) +✔ genesis document serializes deterministically and replays (5.038082ms) +✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.578519ms) +✔ a tampered result, receipt or viewSha fails replay (2.754672ms) +✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.220275ms) +✔ add: defaults for an ordinary seat, privileged extras, refusals (4.423892ms) +✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (5.388769ms) +✔ matrix: release, review round, changes requested and waiting-on-jason (14.263772ms) +✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (9.505118ms) +✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (20.972625ms) +✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.303536ms) +✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1363.105355ms) +✔ matrix: blocked keeps the claim and returns only to previousState (4.447554ms) +✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.614957ms) +✔ field edits: who may change what (5.165574ms) +✔ set issues keeps a logged narrowing of closes (N10) (2.942552ms) +✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.85875ms) +✔ every accepted text renders to nine cells on every row (N8) (20.914489ms) +✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.512522ms) +✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.581623ms) +✔ replay holds every op id to the caller's rule (N11) (5.167698ms) +✔ note: owner, listed reviewer or privileged; empty clears (1.162734ms) +✔ assign moves the claim with the owner; done clears it (3.197846ms) +✔ render is byte-stable and escapes pipes (0.807018ms) +✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.995364ms) +✔ next: resume, then review, then start, then wait, then nothing; lowest id first (19.195788ms) +✔ canonical args make a retry's identity independent of list order (0.309044ms) +✔ manifests, headings and blob ids (0.427786ms) +✔ the migration map: one queue-map block, exact keys (0.572247ms) +✔ every call but `queue` reaches the seat CLI exactly as before A2 (607.731736ms) +✔ `queue` reaches the queue CLI with the rest of the arguments (195.574222ms) +✔ the pre-A2 fixture is the script A2 changed (0.356401ms) +✔ acquire publishes the record by link; release removes only its own lock (13.389715ms) +✔ a kill between the temp write and the link leaves no lock (63.38172ms) +✔ a short or failed temp write refuses and leaves no lock and no temp (12.392891ms) +✔ a link error other than EEXIST refuses (9.631904ms) +✔ an error after the link releases the lock: unreadable gate, failing temp stat (25.868635ms) +✔ a release that fails on a gate path is reported, never a stack trace (P1) (48.706483ms) +✔ a paused holder: another writer waits 10 s, then refuses naming it live (10163.747837ms) +✔ two concurrent unlockers: the second refuses on the gate (39.197091ms) +✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (21.147628ms) +✔ a writer publishing during an unlock, gate first: the writer releases and refuses (19.956698ms) +✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (25.205621ms) +✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (19.540586ms) +✔ the same pid and start on a different boot is mismatch (0.454593ms) +✔ a foreign host is unknown whatever the local pid says; unlock refuses (80.038503ms) +✔ unreadable /proc: classification is unknown and acquire refuses (0.714459ms) +✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.193384ms) +✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (27.989621ms) +✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (37.885324ms) +✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (7.559526ms) +✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (33.37987ms) +✔ the migration map validates and renders the golden genesis table (4.544617ms) +✔ the marked QUEUE.md holds every row and parked item between its markers (1.521251ms) +✔ map-check reports each kind of drift (4.063935ms) +✔ a request posts once as the requester; a retry sends nothing (907.567474ms) +✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (5634.985493ms) +✔ the pre-send checks: GET user must name the requester, under the deadline (2024.877288ms) +✔ the lead's request refuses a token for login sage (1338.651634ms) +✔ the credential file: the seat's own, 0600, no symlink, never the shared default (1228.486095ms) +✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2754.692489ms) +✔ a same-op retry after a kill sends nothing, even with a stale view (3950.371161ms) +✔ a held lock at the outcome exits 3 and names what the transport said (929.89329ms) +✔ late outcomes: after an abandon, and after a resolve with the same or another id (2694.821705ms) +✔ resolve checks the comment: issue, markers, round, candidate and author (1837.280602ms) +✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (748.902483ms) +✔ validateRow checks a request round's shape, which every replayed entry must keep (596.650199ms) +✔ request, changes, a new candidate, approval: every round pinned; no review files (1989.692934ms) +✔ a row with no reviewers opens a round that posts nothing (1237.326447ms) +✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1426.649569ms) +✔ semantics: v1 entries replay as before; review entries need v2 (521.341911ms) +✔ set reviewers refuses the row's owner (2026-09-28) (344.323927ms) +✔ the owner records no verdict, even as a listed reviewer (517.176466ms) +✔ a request comment over the length limit is not sent (588.836789ms) +✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (694.41838ms) +✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2666.764879ms) +✔ genesis: refusals before anything is written (441.184559ms) +✔ genesis: the map must be committed, well formed, with committed briefs and seats (638.17522ms) +✔ genesis: markers, a stray witness, once only; a retry returns the receipt (542.713935ms) +✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (519.374038ms) +✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (742.390057ms) +✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (1175.319818ms) +✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (836.173892ms) +✔ a retried add returns the id it first allocated, after reassignment and after done (1235.189137ms) +✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (796.685316ms) +✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1562.587561ms) +✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (951.858105ms) +✔ add, set reviewers and assign refuse the row's owner as a reviewer (712.267751ms) +✔ the working-brief check: a changed working copy refuses the start and flags next (829.028797ms) +✔ next: resume first, then nothing for an idle seat; needs a seat (390.962001ms) +✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (869.719553ms) +✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1381.457848ms) +✔ a hand edit to queue.json refuses every verb, reads included (798.612258ms) +✔ verify and render --check leave bytes and mtimes unchanged (559.180177ms) +✔ render is byte-stable across runs and repositories (379.44948ms) +✔ snapshot and verify --snapshot (949.105612ms) +✔ usage errors exit 4 (672.356981ms) +✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (392.348745ms) +✔ a rename failure: nothing visible, temp removed (197.725628ms) +✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (325.656857ms) +✔ a directory fsync failure, then sync names the op (421.964441ms) +✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (251.660287ms) +✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (212.645687ms) +✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (294.551614ms) +✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (247.644967ms) +✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (270.768187ms) +✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (343.766488ms) +✔ unlock prints a swapped gate's warning on stderr, the result on stdout (173.697367ms) +✔ a view write that fails keeps the op and reports a stale view (435.79884ms) +✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (724.765935ms) +✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (742.710009ms) +✔ SIGKILL after the witness, before the view: the stale refusal names the op (861.558187ms) +✔ SIGKILL after the view, before the receipt: the retry returns the receipt (712.43739ms) +✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (291.706896ms) +✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1340.212527ms) +✔ a deleted witness: refused after the locked recheck; accept-history records it absent (438.89869ms) +✔ a header edit during a write: the op stands, the view write is skipped with a warning (253.809717ms) +✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (259.923983ms) +✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (309.231838ms) +✔ a writer paused before and after the witness rename: readers see a tail, then a match (379.415949ms) +✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (702.75824ms) +✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (716.014685ms) +✔ the platform check refuses other filesystems (172.883969ms) +✔ tmpfs passes only a test layer that allows it (N5) (267.737966ms) +✔ unlock keeps a multi-line lock record on stdout (P3) (245.081358ms) +ℹ tests 148 +ℹ suites 0 +ℹ pass 148 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 34710.608382 diff --git a/agents/darkwing/work/queue-51-review/r2/out/node-webui.txt b/agents/darkwing/work/queue-51-review/r2/out/node-webui.txt new file mode 100644 index 00000000..0ab25cf2 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/out/node-webui.txt @@ -0,0 +1,31 @@ +✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3472.217224ms) +Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286} +✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3656.128933ms) +✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4070.705241ms) +✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (246.660525ms) +✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (71.125718ms) +✔ bus refusals map to statuses and never carry a path or a stack (31.337264ms) +✔ humanCall speaks the human transport protocol without blocking the server (2067.223133ms) +✔ busReader takes --business, else the live bus host, else refuses (120.163476ms) +✔ humanCall kills a transport that runs past its timeout (527.949051ms) +✔ serve refuses a --business value that is not a business id (171.747344ms) +✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3449.547014ms) +✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1645.808442ms) +✔ conversation view: a newer session with no readable history keeps the marker (1186.148336ms) +✔ conversation view: seats without history say so and offer no reply (994.798861ms) +✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3399.383957ms) +✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53317.153671ms) +✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3351.849099ms) +✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22166.043857ms) +✔ loopback host and board origin fail closed (8.758778ms) +✔ real board fixture passes through WebUI; assets and isolated seen/reply work (111.560154ms) +✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (86.007953ms) +✔ unreachable board reports URL; CLI rejects unsupported options (823.950155ms) +ℹ tests 22 +ℹ suites 0 +ℹ pass 22 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 53688.255416 diff --git a/agents/darkwing/work/queue-51-review/r2/out/summary.txt b/agents/darkwing/work/queue-51-review/r2/out/summary.txt new file mode 100644 index 00000000..7aee4e07 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/out/summary.txt @@ -0,0 +1,17 @@ +start 2026-10-10T06:43:17Z shims=0 units=0 +node-conversation exit=0 ℹ pass 177 ℹ fail 0 shims-left=0 units=1 +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-10T06:46:47Z shims-left=0 units=0 +DONE +node-control-board exit=0 ℹ pass 124 ℹ fail 0 +node-queue exit=0 ℹ pass 148 ℹ fail 0 +DONE2 diff --git a/agents/darkwing/work/queue-51-review/r2/out/test-auth.txt b/agents/darkwing/work/queue-51-review/r2/out/test-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r2/out/test-conductor.txt b/agents/darkwing/work/queue-51-review/r2/out/test-conductor.txt new file mode 100644 index 00000000..62afc513 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r2/out/test-config.txt b/agents/darkwing/work/queue-51-review/r2/out/test-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r2/out/test-discord.txt b/agents/darkwing/work/queue-51-review/r2/out/test-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r2/out/test-extension-package.txt b/agents/darkwing/work/queue-51-review/r2/out/test-extension-package.txt new file mode 100644 index 00000000..98292138 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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: 2230130 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/r2/out/test-foundation.txt b/agents/darkwing/work/queue-51-review/r2/out/test-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r2/out/test-queue.txt b/agents/darkwing/work/queue-51-review/r2/out/test-queue.txt new file mode 100644 index 00000000..a6e196ad --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r51b/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/r2/out/test-release-docker.txt b/agents/darkwing/work/queue-51-review/r2/out/test-release-docker.txt new file mode 100644 index 00000000..7202b85d --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r2/out/test-release.txt b/agents/darkwing/work/queue-51-review/r2/out/test-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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/r2/out/test-task.txt b/agents/darkwing/work/queue-51-review/r2/out/test-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/darkwing/work/queue-51-review/r2/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-r2.md b/agents/darkwing/work/queue-51-review/review-r2.md new file mode 100644 index 00000000..1620353e --- /dev/null +++ b/agents/darkwing/work/queue-51-review/review-r2.md @@ -0,0 +1,131 @@ +# Row 51, cohort release follow-ups, round 2 review (Darkwing) + +Issue #1537, request comment 27072, notes comment 27073, queue revs +304-306. My round 1 was comment 27071 (changes); Sage ruled to move the +call and keep the text. Base `b7e9efb7`, which changes nothing under +`packages/` or `scripts/` since round 1's `bbb2167f`. Candidate manifest +sha256 +`c34039dc0d52b965bd73cc9f079ae7b253140d77713a3323abc4743f359f0c02`, +the same 5 files as round 1. `ctrl-child.mjs` is unchanged from round 1. + +Verdict: **approve**. `#recover` now retries the release after the +confirmation check and before the acquire, so a refused `recover` has no +side effect. The packet's K6 line and the README now match the code. R9 +pins the order: every placement of the release above the confirmation +check that I tried (round 1's, above the stop-proof check, and between +the target and confirmation checks) fails R9. The three notes Dewey took +from round 1 each kill the mutant they were aimed at. `noseat` still +survives and the packet lists it as a follow-up, which I accept. + +## Method + +- Detached worktree at `b7e9efb7`, 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 + (`r2/mut/manifest-after.txt`). +- I read the round 1 to round 2 diff in full, `#recover` again from top to + bottom, R7 and R9-R11, the README bullet, the packet's "Round 2" + section, and the `recover` command in + `docs/plans/chat-01/contracts.schema.json`. +- 11 mutants (`r2/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/r51b/tmp`, `gate.sh` with +`DOCKER_HOST=unix:///nonexistent.sock`, 06:43:17Z to 06:46:47Z, then +control-board and queue (node). + +## 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 +round 1. Dewey's 98/0 ran them. The one `mosaic-chat-*` unit counted right +after the conversation suite was gone at the gate's end, and none was +listed after the mutants. + +## The recover order + +``` +if (... !this.#stopped(s)) return refused("stop-proof"); +... pins, refused(ENGINE_PIN_MISMATCH) ... +... session leaf and branch, refused("target") ... +if (!this.#checkConfirmation(r, c, "recover")) return refused("confirmation"); +// After every check, so a refused recover changes nothing (K6). +await this.#releaseListed("recover", this.claim); +... acquire ... +``` + +That's option 1 from my round 1 review. R9 now sends two recovers with the +socket back before the confirmed one: one with a confirmation ID that was +never issued, and one with a confirmation confirmed for `force-stop`. Both +come back `confirmation`, `evidence.releases` is still the one `unavailable` +entry, and the unit is still active. The packet says a recover with no +`confirmation` field is refused `malformed` before `#recover`. I checked: +the schema's `recover` command lists `stop` and `confirmation` as required, +and the controller's `malformedRequest` runs before dispatch and refuses a +`recover` whose fields don't match `SHAPES.recover` (`stop` and +`confirmation`, both IDs). + +The `await` between the confirmation check and the acquire lets another +command run in between. That isn't new: the acquire was already awaited, +so two recovers could interleave before this row. + +## Mutants + +| Mutant | Change | Conversation | Result | +|---|---|---|---| +| recconf | the release right after the stop-proof check (round 1's order) | 176/1 | killed: R9 | +| recbefore | the release above the stop-proof check | 176/1 | killed: R9 | +| recpreconf | the release after the target check, before the confirmation | 176/1 | killed: R9 | +| norecover | recover releases nothing | 176/1 | killed: R9 | +| nodelete | `#releaseListed` never drops an earlier result | 176/1 | killed: R9 | +| startnoawait | the free-path release isn't awaited | 176/1 | killed: R7 | +| nolookup | `#releaseListed` sends a request for a unit already absent | 176/1 | killed: R11 | +| closeproof | close skips the signal only while `#stopped()` still accepts the stop | 176/1 | killed: R10 | +| closekill | close signals after a proven stop | 175/2 | killed: R10, R11 | +| recafteracq | the release moves to just after the acquire | 177/0 | survives: see note 1 | +| noseat | the free path checks only the session head | 177/0 | survives: the packet's follow-up | + +`recpreconf` is the one placement Dewey's table doesn't have. It's the +closest wrong order, past every check except the confirmation, and R9 +catches it like the others. + +## Notes (not blocking) + +1. `recafteracq` survives. The packet's K17 line says the recover release + runs "after them and before the acquire". After the acquire it still + acts on `this.claim`, the stopped record the acquire doesn't replace, so + no test can tell the two apart from outside. I see no safety difference + either: both orders release only a proven-stopped cohort, and both run + only on a recover that passed every check. The one difference is a + recover whose acquire throws: before the acquire, the release has + already happened. That's fine for a recover that passed its + confirmation. R7's trick (the launcher reads `evidence.releases.length`) + would pin the order if anyone wants it. I don't need it. +2. `noseat` survives, as the packet says. It needs a second session on the + same seat. I agree it's a follow-up and not this row. +3. Dewey's other follow-ups stand as in round 1. The `uncertain` binding + whose engine exited on its own is row 52's. + +## Files + +- `r2/candidate-manifest.sha256`: copy of Dewey's. +- `r2/gate.sh`, `r2/out/`: suite runs and `summary.txt`. +- `r2/mut/`: mutant definitions, runner, per-mutant output and + shims-left lists, `summary.txt`, and the manifest check after the runs.