docs(review): row 47 round 1 review packet, approve (darkwing)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
dfd9be08c4a31ee536bbcd9bfd8e73cd8baa4114086b55845627ea0c97023e2a agents/dewey/work/queue-47/evidence.md
|
||||
061ca09644d03690c92b2d09f3b13d407f194fe0b590125692c359d10bace395 packages/conversation/tests/claim.test.mjs
|
||||
9bb32c730e6fd1d6648ff623cd6ded71cda300ede2d618e28ee6b682626d4d01 packages/conversation/tests/cohort.test.mjs
|
||||
896f09586916ffdb399b2b5bd04770473a6dd62938d1e9cc0a7e0239f32a36ef packages/conversation/tests/flows.test.mjs
|
||||
ca003075749dec9368c91f282c8218d602eeb8c6290282a884876be5af2d87e6 packages/conversation/tests/harness.mjs
|
||||
2ae5d7b8119f29654e1672d1948992be79e973049a50da99250de7ade26b2071 packages/conversation/tests/races.test.mjs
|
||||
@@ -0,0 +1,6 @@
|
||||
agents/dewey/work/queue-47/evidence.md
|
||||
packages/conversation/tests/claim.test.mjs
|
||||
packages/conversation/tests/cohort.test.mjs
|
||||
packages/conversation/tests/flows.test.mjs
|
||||
packages/conversation/tests/harness.mjs
|
||||
packages/conversation/tests/races.test.mjs
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
export TMPDIR=~/darkwing-scratch/r47a/tmp DOCKER_HOST=unix:///nonexistent.sock
|
||||
cd ~/darkwing-scratch/r47a/wt
|
||||
O=~/darkwing-scratch/r47a/out
|
||||
: > $O/summary.txt
|
||||
shims() { ps -eo pid,args | grep -F "$TMPDIR" | grep -F shim.mjs | grep -v grep | wc -l; }
|
||||
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)" >> $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 "shims-left-end=$(shims)" >> $O/summary.txt
|
||||
echo DONE >> $O/summary.txt
|
||||
@@ -0,0 +1,196 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (230.761469ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (35.283345ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.246256ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (666.451415ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (419.502539ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (259.427764ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (243.237404ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (216.032408ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.493472ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12101.571707ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (35852.52336ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (364.603499ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (157.89886ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (696.888506ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (353.745861ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (432.046961ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (176.123441ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (237.115041ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (443.951742ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1335.729283ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (399.160544ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (128.725923ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (69.244187ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.523388ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.102658ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.859532ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (25.021175ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2907.900269ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (662.084573ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2648.481561ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4971.590724ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2671.091036ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2543.899927ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2497.760482ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5061.043083ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (944.475903ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (579.802852ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (510.816848ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (853.259964ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (570.67324ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (748.384126ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3374.04811ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (27.290911ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (633.85929ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (650.888402ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (103.278418ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.447839ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (219.066131ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (486.569612ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (237.738849ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (344.619515ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (230.932458ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.452415ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (242.54138ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (326.810099ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (250.642243ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (222.955355ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (302.713109ms)
|
||||
✔ 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 (343.538094ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.0646ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (302.801926ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (249.456734ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (315.374632ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (225.411308ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.373986ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.257853ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.32094ms)
|
||||
✔ 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.481082ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.859499ms)
|
||||
✖ 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) (1.169348ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (6.289639ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.414834ms)
|
||||
✔ 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.419486ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.101127ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (224.439992ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (265.687996ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (602.750029ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (980.633425ms)
|
||||
✔ H4: self-takeover is refused (211.637746ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (506.871423ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1488.148474ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (257.919317ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (807.188663ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (345.183361ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (230.52151ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (187.355213ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (695.840348ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (392.932221ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (219.444268ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (986.019029ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (368.285501ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (809.335038ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (217.250935ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (337.785107ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.629978ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (681.233021ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (588.806469ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (712.524709ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2943.496164ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (678.292339ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.16412ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (8.500467ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.946212ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (3.038266ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.460723ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (2.377455ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (3.037382ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (1.323094ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (2.118799ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (5.420763ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.4095ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.711012ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (6.202576ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (8.351461ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.80748ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.879545ms)
|
||||
✔ F9: registrations never add or redirect a root (1.819782ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.267714ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.734978ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.160641ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.942174ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.400315ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.72091ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (541.577382ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.290547ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.426317ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.189166ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.018126ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.785298ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (734.907993ms)
|
||||
✔ the engine pin holds for the installed package (3.14919ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (289.737014ms)
|
||||
✔ 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) (607.426468ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (240.104396ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (819.982387ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (520.700411ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (292.365153ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.764736ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (410.320626ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (234.058416ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (327.369494ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (752.757501ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1969.460632ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (228.669663ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (312.846973ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (221.554358ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (269.807001ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (554.763895ms)
|
||||
✔ N10: fake conformance (32.742057ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (465.53822ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (311.07635ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (366.274019ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (379.816865ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (381.8195ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (380.794236ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (215.48326ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (368.478508ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (764.711894ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (601.979351ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (519.115829ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (210.54534ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (191.338939ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (189.870649ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (237.835355ms)
|
||||
✔ 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 (157.620797ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 164
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 58028.081921
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/flows.test.mjs:714:1
|
||||
✖ 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) (1.169348ms)
|
||||
AssertionError [ERR_ASSERTION]: ["\u0014","\u000f","hi\r"]
|
||||
+ actual - expected
|
||||
|
||||
+ []
|
||||
- [
|
||||
- 'hi'
|
||||
- ]
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/flows.test.mjs:726:12)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [],
|
||||
expected: [ 'hi' ],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (253.899662ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.284113ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.860532ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (734.363814ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (524.38725ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (252.799759ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (222.741713ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (234.15361ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.493404ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12258.436675ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (32349.669618ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (272.642236ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (147.477768ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (480.56355ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (338.33334ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (399.586115ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (149.751919ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (236.949804ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (387.390616ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1038.478309ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (237.992787ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (119.660597ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (80.974265ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.444071ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.109812ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.86441ms)
|
||||
✖ no shim from this file's tests is left running for the after hook (5035.26209ms)
|
||||
✖ /home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/claim.test.mjs (5043.339958ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3302.420094ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (472.970914ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2796.728057ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5105.353597ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2543.819351ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2510.616024ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2475.701534ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5063.223619ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (771.359208ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (645.101545ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (506.62451ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (829.124385ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (420.127538ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (685.425895ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3650.969116ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (50.323904ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (469.843386ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (401.628845ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.176682ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (28.57982ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (249.951436ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (230.734553ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (529.84075ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (468.686749ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (237.030606ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.410276ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (217.020169ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (315.308644ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (240.266481ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (216.341803ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (277.942621ms)
|
||||
✔ 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.881183ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.956364ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (209.424433ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (233.942839ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (240.21204ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (202.309814ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.473741ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.344905ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.345713ms)
|
||||
✔ 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.509785ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.230161ms)
|
||||
✔ 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.535526ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.767751ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.427819ms)
|
||||
✔ 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.374154ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.124458ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (253.515467ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (294.243499ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (643.849856ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1035.077009ms)
|
||||
✔ H4: self-takeover is refused (220.514251ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (513.050569ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1044.31887ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (200.880629ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (646.165518ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (299.259321ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (204.891687ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (210.61934ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (608.700028ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (293.875373ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (207.822911ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (907.20909ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (383.22996ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (780.261155ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (314.578713ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (492.689541ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.638149ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (745.520193ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (614.542673ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (775.55878ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2983.804441ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (663.639159ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (29.149662ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.901379ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.757561ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (3.006657ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (4.500776ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.961886ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.558189ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (1.380572ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (2.109752ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.787822ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.931861ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.60544ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.016363ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.376863ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.418495ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.608935ms)
|
||||
✔ F9: registrations never add or redirect a root (2.387521ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.782722ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.704275ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.158477ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.217859ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.398999ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.772587ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (562.681896ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.139319ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.62126ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (3.23556ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.209457ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.226314ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (787.451304ms)
|
||||
✔ the engine pin holds for the installed package (2.048735ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (285.366263ms)
|
||||
✔ 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) (579.211254ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (272.40564ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (577.591802ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (946.761723ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (282.830757ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.001149ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (399.29855ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (230.660155ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (301.688325ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (545.551867ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1879.973217ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (206.93602ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (301.335987ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (216.121148ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (246.935251ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (338.923698ms)
|
||||
✔ N10: fake conformance (32.84403ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (463.157846ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (304.192068ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (300.327975ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (383.033758ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (376.977901ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (372.577696ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (279.493416ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (661.735582ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (826.188509ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (576.149478ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (487.44522ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (249.137762ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (216.081275ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (213.406251ms)
|
||||
✔ 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.431708ms)
|
||||
✔ 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 (235.869084ms)
|
||||
ℹ tests 166
|
||||
ℹ suites 0
|
||||
ℹ pass 164
|
||||
ℹ fail 2
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 60982.361112
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/claim.test.mjs:714:1
|
||||
✖ no shim from this file's tests is left running for the after hook (5035.26209ms)
|
||||
AssertionError [ERR_ASSERTION]: a test left its shim running
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ {
|
||||
+ pid: 3459119,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-afterleak/chat03-lQNQDH/f55/sock/shim-cede1ee324e5.sock',
|
||||
+ unit: 'mosaic-chat-cede1ee324e5b263ef2671152'
|
||||
+ }
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/claim.test.mjs:715:10)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { pid: 3459119, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-afterleak/chat03-lQNQDH/f55/sock/shim-cede1ee324e5.sock', unit: 'mosaic-chat-cede1ee324e5b263ef2671152' } ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
|
||||
test at packages/conversation/tests/claim.test.mjs:28:1
|
||||
✖ /home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/claim.test.mjs (5043.339958ms)
|
||||
AssertionError [ERR_ASSERTION]: no shim outlives this file (#1533)
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ {
|
||||
+ pid: 3459119,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-afterleak/chat03-lQNQDH/f55/sock/shim-cede1ee324e5.sock',
|
||||
+ unit: 'mosaic-chat-cede1ee324e5b263ef2671152'
|
||||
+ }
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/claim.test.mjs:32:10)
|
||||
at async TestHook.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.runHook (node:internal/test_runner/test:1289:9)
|
||||
at async after (node:internal/test_runner/test:1342:9)
|
||||
at async Test.run (node:internal/test_runner/test:1428:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { pid: 3459119, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-afterleak/chat03-lQNQDH/f55/sock/shim-cede1ee324e5.sock', unit: 'mosaic-chat-cede1ee324e5b263ef2671152' } ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
3459119 /usr/bin/node /home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/src/shim.mjs --socket /home/jwoltje/darkwing-scratch/r47a/tmp/mut-afterleak/chat03-lQNQDH/f55/sock/shim-cede1ee324e5.sock -- /usr/bin/node /home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/fake-pi.mjs --mode rpc --no-extensions --no-prompt-templates --no-themes --no-approve --session /home/jwoltje/darkwing-scratch/r47a/tmp/mut-afterleak/chat03-lQNQDH/f55/proj/.pi/state/fixture-seat/sessions/s1.jsonl
|
||||
@@ -0,0 +1,205 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (197.049296ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (30.2567ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.869101ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (345.31181ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (422.999536ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (331.462139ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (269.896862ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (226.565456ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.459269ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11410.993529ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (33468.852499ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (278.035112ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (156.636284ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (648.670807ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (282.454964ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (359.105989ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (98.174892ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (200.317068ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (257.96069ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (719.726591ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (173.907622ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (88.540111ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (69.986196ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.40838ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.037676ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.732583ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (25.039017ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2950.569631ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (451.497915ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2781.966793ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4839.738336ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2514.094444ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2517.229628ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2437.649908ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5021.280751ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (959.412745ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (554.748075ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (598.302911ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (646.325558ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (328.297709ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (524.693662ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3271.735696ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (33.382515ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (360.808345ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (361.996968ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.528117ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (28.074449ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (194.578162ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (188.929563ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (186.630794ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (310.239901ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (250.607896ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.453839ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (325.324105ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (366.090081ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (225.087215ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (192.31515ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (209.298056ms)
|
||||
✔ 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 (218.342388ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.97722ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (207.199358ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (226.432261ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (251.463208ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (201.15355ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.426921ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.272057ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.326321ms)
|
||||
✔ 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.493318ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.929698ms)
|
||||
✔ 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.489135ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.824779ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.428027ms)
|
||||
✔ 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.351569ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.110321ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (237.286641ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (230.878494ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (277.834974ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1060.632333ms)
|
||||
✔ H4: self-takeover is refused (224.791538ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (446.212191ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1052.06213ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (231.205097ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (660.604274ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (278.293002ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (207.032141ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (196.251993ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (727.62471ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (370.396901ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (192.209941ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (876.947035ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (321.051889ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (758.666111ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (360.368072ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (335.060909ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.612822ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (661.027296ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (566.594343ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (708.560077ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2938.033537ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (655.993875ms)
|
||||
✖ no shim from this file's tests is left running for the after hook (5056.941433ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (9.45792ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.653489ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.771711ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.721783ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.744085ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.950975ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (1.033733ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.299469ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.631965ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.083972ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.777326ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.450746ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.504334ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (11.504271ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.590604ms)
|
||||
✔ F9: registrations never add or redirect a root (2.60161ms)
|
||||
✔ F10: a header cwd naming another project is refused (5.312424ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.833801ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.576443ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.031185ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.540383ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (1.055277ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (495.761506ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.530977ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.508889ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.332009ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.215739ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.130551ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (791.701227ms)
|
||||
✔ the engine pin holds for the installed package (2.128908ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (263.839834ms)
|
||||
✔ 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) (503.901361ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (264.144541ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (460.491405ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (459.366987ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (331.634788ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.888495ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (490.102814ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (213.93758ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (293.015714ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (477.191806ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1888.727614ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (203.998308ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (316.971117ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (282.097455ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (256.140276ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (477.083588ms)
|
||||
✔ N10: fake conformance (32.934542ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (450.496013ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (281.090048ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (298.332038ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (355.356665ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (358.037348ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (524.641792ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (195.587322ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (285.42895ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (632.572932ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (497.638133ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (464.937522ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (222.460525ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (153.565035ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (166.396372ms)
|
||||
✔ 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 (224.677414ms)
|
||||
✔ 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 (248.72541ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 164
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53159.579855
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/races.test.mjs:902:1
|
||||
✖ no shim from this file's tests is left running for the after hook (5056.941433ms)
|
||||
AssertionError [ERR_ASSERTION]: a test left its shim running
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ {
|
||||
+ pid: 3568275,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-h22noreap/chat03-9zq6wq/f33/sock/shim-cfdfa3433345.sock',
|
||||
+ unit: 'mosaic-chat-cfdfa3433345b9db00c5d3051'
|
||||
+ },
|
||||
+ {
|
||||
+ pid: 3568508,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-h22noreap/chat03-9zq6wq/f33/sock/shim-cd552eea1f4f.sock',
|
||||
+ unit: 'mosaic-chat-cd552eea1f4f3842f83cdde65'
|
||||
+ }
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/races.test.mjs:903:10)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { pid: 3568275, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-h22noreap/chat03-9zq6wq/f33/sock/shim-cfdfa3433345.sock', unit: 'mosaic-chat-cfdfa3433345b9db00c5d3051' }, { pid: 3568508, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-h22noreap/chat03-9zq6wq/f33/sock/shim-cd552eea1f4f.sock', unit: 'mosaic-chat-cd552eea1f4f3842f83cdde65' } ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (210.899084ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (29.31549ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.448014ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (343.333366ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (402.030356ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (229.874766ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (225.367275ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (235.538338ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.510001ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11107.098528ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (34234.25014ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (257.480277ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (143.029294ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (640.332526ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (280.540927ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (344.303521ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (100.06914ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (193.668152ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (260.464092ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (719.818824ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (176.592675ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (84.681532ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (68.297527ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.391231ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.076531ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.691738ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (24.993244ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2926.997693ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (457.071569ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2764.632052ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4847.049015ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2624.498105ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2504.544818ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2404.966985ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5364.683799ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (760.038648ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (786.324974ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (488.827654ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (648.931098ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (535.972437ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (721.376017ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3325.570656ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (21.89574ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (369.227467ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (361.962609ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (85.614309ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.492113ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (200.045294ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (185.609228ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (183.997293ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (339.6938ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (186.03016ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.412374ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (172.208693ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (328.732036ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (228.009808ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (195.782075ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (215.719368ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (212.909605ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.147188ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (201.279039ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (220.788048ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (250.112806ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (194.326465ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.405847ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.253462ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.309444ms)
|
||||
✔ 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.464404ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.10644ms)
|
||||
✔ 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.456697ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.716844ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.439276ms)
|
||||
✔ 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.339054ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.102817ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (219.772943ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (247.163097ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (270.012137ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (771.823938ms)
|
||||
✔ H4: self-takeover is refused (243.410382ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (456.213319ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1034.647794ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (233.37577ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (707.537006ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (265.35379ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (194.180998ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (182.89918ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (540.720156ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (289.805567ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (188.196907ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (876.45361ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (364.162773ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (740.85462ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (196.962746ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (339.055104ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.612248ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (682.735127ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (554.789216ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (707.976793ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2900.763338ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (558.159465ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.551198ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.823937ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.618154ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.043225ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.654588ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.853218ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.892136ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (1.156663ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.916826ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.844588ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (6.50136ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.149952ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.701323ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.772768ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.21024ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.860667ms)
|
||||
✔ F9: registrations never add or redirect a root (1.777036ms)
|
||||
✔ F10: a header cwd naming another project is refused (7.824365ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (1.222698ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (6.650439ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.648522ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.562117ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.796403ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (535.261071ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.446942ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.359388ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.192692ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.017901ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.868379ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (732.019885ms)
|
||||
✔ the engine pin holds for the installed package (2.291951ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (273.447098ms)
|
||||
✔ 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) (580.641872ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (246.192927ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (450.247343ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (488.211935ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (216.62409ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (19.979108ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (376.258015ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (224.504275ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (299.424625ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (480.041118ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1885.455756ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (186.703798ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (270.4736ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (187.391958ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (156.128647ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (401.559088ms)
|
||||
✔ N10: fake conformance (32.345984ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (444.806874ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (270.811479ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (301.230822ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (369.074885ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (346.504687ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (343.343183ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (199.261583ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (316.774796ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (626.019436ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (483.717534ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (433.204563ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (199.668666ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (166.130174ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (200.872925ms)
|
||||
✔ 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 (212.757521ms)
|
||||
✔ 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 (199.990162ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 165
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53439.906087
|
||||
@@ -0,0 +1,200 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (202.054486ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (29.094903ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (20.521371ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (380.265738ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (383.77194ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (235.297995ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (178.708428ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (191.616901ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.488977ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11459.462545ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (32312.775307ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (262.995988ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (145.295044ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (620.604825ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (277.742463ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (357.582737ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (135.674806ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (194.274656ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (300.792706ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1028.727876ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (213.849293ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (92.99746ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (67.615031ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.586644ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.081281ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.22829ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (24.991113ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2945.629077ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (455.109288ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2781.203996ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4882.741961ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2545.016691ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2654.869004ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2370.187002ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5552.934552ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (788.269939ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (712.605732ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (806.214727ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (692.985289ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (332.069099ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (463.128132ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3254.545765ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (21.722265ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (278.326856ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (292.170135ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (69.124067ms)
|
||||
✖ no shim from this file's tests is left running for the after hook (5042.966648ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (194.790194ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (190.066625ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (182.2687ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (315.705683ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (182.793024ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.416493ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (186.923301ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (282.324841ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (210.171112ms)
|
||||
✔ 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.893319ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (224.590288ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (220.473361ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.02041ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (204.112998ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (214.878204ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (252.623854ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (199.609442ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.42963ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.296061ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.320954ms)
|
||||
✔ 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.481725ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.849683ms)
|
||||
✔ 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.444594ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.71464ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.328147ms)
|
||||
✔ 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.329988ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.104019ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (224.528016ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (243.085787ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (269.197312ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (816.20328ms)
|
||||
✔ H4: self-takeover is refused (178.587331ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (438.883667ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1060.31653ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (223.711706ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (662.952093ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (293.495194ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (193.47591ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (197.665643ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (592.405485ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (295.824203ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (250.953389ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (978.022423ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (370.058061ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (768.589881ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (202.773991ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (346.678335ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.658992ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (671.389147ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (587.714656ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (707.767483ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2964.584779ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (590.347364ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (25.878234ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.168172ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.700461ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.581868ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.428857ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.316679ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.848228ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (1.202433ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.615224ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.181542ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.451603ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.915774ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.416033ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.368711ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.044047ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.730808ms)
|
||||
✔ F9: registrations never add or redirect a root (1.692061ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.311864ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (1.06521ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.958754ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.190111ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.705023ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.921494ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (503.65243ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.346234ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.423213ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.18304ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.023734ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.835462ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (744.075418ms)
|
||||
✔ the engine pin holds for the installed package (1.714045ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (266.329572ms)
|
||||
✔ 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) (514.394265ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (240.931026ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (452.163235ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (457.981922ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (220.293101ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (20.779992ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (324.014318ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (193.956062ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (293.795022ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (512.045177ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1880.726547ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (195.495573ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (276.81879ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (227.736397ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (235.102568ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (436.369737ms)
|
||||
✔ N10: fake conformance (32.547618ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (478.496775ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (300.483864ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (342.840723ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (364.51853ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (375.392696ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (354.980863ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (202.914908ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (290.893238ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (727.756146ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (508.176217ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (487.479705ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (192.908547ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (172.475922ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (181.295263ms)
|
||||
✔ 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 (214.730578ms)
|
||||
✔ 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 (212.086389ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 164
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 52233.099536
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/cohort.test.mjs:748:1
|
||||
✖ no shim from this file's tests is left running for the after hook (5042.966648ms)
|
||||
AssertionError [ERR_ASSERTION]: a test left its shim running
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ {
|
||||
+ pid: 3538170,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-k19leak/chat03-GfMOSY/f24/sock/k19.sock',
|
||||
+ unit: 'mosaic-chat-ccf3b9f715753becf8b551870'
|
||||
+ }
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/cohort.test.mjs:749:10)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { pid: 3538170, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-k19leak/chat03-GfMOSY/f24/sock/k19.sock', unit: 'mosaic-chat-ccf3b9f715753becf8b551870' } ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
agents/dewey/work/queue-47/evidence.md: OK
|
||||
packages/conversation/tests/claim.test.mjs: OK
|
||||
packages/conversation/tests/cohort.test.mjs: OK
|
||||
packages/conversation/tests/flows.test.mjs: OK
|
||||
packages/conversation/tests/harness.mjs: OK
|
||||
packages/conversation/tests/races.test.mjs: OK
|
||||
@@ -0,0 +1,33 @@
|
||||
# mutate.py <id>: apply one named mutant (each edit exact text, must match once). Run from the tree root.
|
||||
import sys
|
||||
C = "packages/conversation/"
|
||||
NOPROBE = (C + "tests/claim.test.mjs", " // The force stop leaves the probe's shim running: nothing sends `release`.\n reap(probe);\n", "")
|
||||
M = {
|
||||
"Mr": [(C + "src/terminal.mjs", " holds = this.held !== null;\n", " holds = false;\n")],
|
||||
"noprobe": [NOPROBE],
|
||||
"now13": [(C + "tests/claim.test.mjs", " // The force stop leaves the shim running: nothing sends `release`.\n reap(fx);\n", "")],
|
||||
"afterleak": [NOPROBE, (C + "tests/claim.test.mjs", " for (const fx of reaped) reap(fx);\n killShims();\n const left", " const left")],
|
||||
# The sweep alone, with no records: noprobe, and after doesn't reap the fixtures.
|
||||
"sweeponly": [NOPROBE, (C + "tests/claim.test.mjs", " for (const fx of reaped) reap(fx);\n killShims();\n const left", " killShims();\n const left")],
|
||||
# The sweep kills by PID only, no scope kill: noprobe and after without reap.
|
||||
"sweeppid": [NOPROBE, (C + "tests/claim.test.mjs", " for (const fx of reaped) reap(fx);\n killShims();\n const left", " killShims();\n const left"),
|
||||
(C + "tests/harness.mjs", " if (s.unit) spawnSync(\"systemctl\", [\"--user\", \"kill\", \"--signal=SIGKILL\", `${s.unit}.scope`], { stdio: \"ignore\", timeout: 5000 });\n", "")],
|
||||
# K19 leaves its scope: no kill, no release.
|
||||
"k19leak": [(C + "tests/cohort.test.mjs",
|
||||
" const killed = await shimRequest(socketPath, \"kill\", { timeoutMs: 5000 }, 8000);\n const released = killed.ok ? await shimRequest(socketPath, \"release\") : killed;\n if (!released.ok) spawnSync(\"systemctl\", [\"--user\", \"kill\", \"--signal=SIGKILL\", `${unitName}.scope`], { stdio: \"ignore\", timeout: 5000 });\n await proc.exited;\n assert.equal(killed.ok, true, JSON.stringify(killed));\n assert.equal(released.ok, true, JSON.stringify(released));\n",
|
||||
" void proc;\n")],
|
||||
# H23 doesn't reap its fixture.
|
||||
"h23noreap": [(C + "tests/races.test.mjs", " await b.exited;\n reap(fx);\n});\n\n// Last in the file.", " await b.exited;\n});\n\n// Last in the file.")],
|
||||
# H22 (a scope, force-stopped, then relaunched) doesn't reap its fixture.
|
||||
"h22noreap": [(C + "tests/races.test.mjs", " await b.exited;\n reap(fx);\n});\n\ntest(\"H23", " await b.exited;\n});\n\ntest(\"H23")],
|
||||
# reap() without the sweep: the backstop alone.
|
||||
"reapnosweep": [(C + "tests/harness.mjs", " // calls above didn't reach: neither result is checked.\n killShims(fx.base);\n", " // calls above didn't reach: neither result is checked.\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)
|
||||
@@ -0,0 +1,200 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (282.937597ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (50.093746ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.74016ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (520.780811ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (443.539556ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (249.091677ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (215.670313ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (234.14316ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.617411ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (13219.243338ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (35043.20244ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (373.142977ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (475.241878ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (622.145753ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (445.37713ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (519.884557ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (125.014355ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (212.933884ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (326.08032ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (906.246954ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (213.52989ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (119.253093ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (78.067386ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.440182ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.111808ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.13747ms)
|
||||
✖ no shim from this file's tests is left running for the after hook (5046.252437ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3104.602351ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (697.532535ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (3047.740898ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5198.390202ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2519.122514ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2573.295712ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2493.738315ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5195.842609ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (859.9822ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (742.744435ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (561.834231ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (901.421249ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (463.846568ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (715.309839ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3391.404111ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (26.042965ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (523.024463ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (330.55586ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.540287ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.566716ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (286.168512ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (377.747436ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (215.200155ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (326.602654ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (230.455005ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.440313ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (215.740333ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (330.41535ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (245.354601ms)
|
||||
✔ 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.812654ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (246.631562ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (252.64731ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.067682ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (451.425124ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (316.454708ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (311.885054ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (292.804535ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.420948ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.255838ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.327912ms)
|
||||
✔ 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.481876ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.289223ms)
|
||||
✔ 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.486287ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.783371ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.39157ms)
|
||||
✔ 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.34537ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.0984ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (272.955772ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (340.814472ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (451.028642ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (908.055698ms)
|
||||
✔ H4: self-takeover is refused (227.773618ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (506.331615ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1553.972775ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (374.170056ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1281.70912ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (388.891311ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (215.368506ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (236.172724ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (690.882483ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (337.15436ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (229.432724ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1197.075582ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (505.809962ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1003.411377ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (186.620806ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (304.074651ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.660476ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (689.760131ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (579.201376ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (711.583219ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2954.117441ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (676.012514ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (25.886638ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.588771ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.703565ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.787985ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.64103ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.443367ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.991457ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.794714ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.278564ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.372104ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.185505ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.862704ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.223447ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.438179ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (8.015533ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.968255ms)
|
||||
✔ F9: registrations never add or redirect a root (2.093746ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.510677ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.728622ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.254575ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.091962ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.374193ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.933001ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (509.469711ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.303588ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (4.26981ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.105312ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.036966ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.788252ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (811.987754ms)
|
||||
✔ the engine pin holds for the installed package (1.836325ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (267.238939ms)
|
||||
✔ 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) (598.801528ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (276.924431ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (749.402112ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (498.688556ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (266.07953ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.947376ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (394.118829ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (232.943051ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (324.480381ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (655.833704ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (2170.238581ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (305.14733ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (342.485459ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (180.111325ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (290.142357ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (428.925667ms)
|
||||
✔ N10: fake conformance (34.033601ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (529.657483ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (313.788867ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (391.601448ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (484.338398ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (489.422664ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (497.122206ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (296.008524ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (467.081721ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (734.233914ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (533.918507ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (502.012851ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (205.405617ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (193.442972ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (185.960475ms)
|
||||
✔ 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 (195.282092ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (203.424206ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 164
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 63077.020588
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/claim.test.mjs:716:1
|
||||
✖ no shim from this file's tests is left running for the after hook (5046.252437ms)
|
||||
AssertionError [ERR_ASSERTION]: a test left its shim running
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ {
|
||||
+ pid: 3407906,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-noprobe/chat03-Q8oINH/f55/sock/shim-c32a2893a970.sock',
|
||||
+ unit: 'mosaic-chat-c32a2893a9706deb3bec82ca0'
|
||||
+ }
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/claim.test.mjs:717:10)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { pid: 3407906, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-noprobe/chat03-Q8oINH/f55/sock/shim-c32a2893a970.sock', unit: 'mosaic-chat-c32a2893a9706deb3bec82ca0' } ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (198.698815ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.862657ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.889047ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (485.604813ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (365.231889ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (232.065585ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (181.538597ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (183.821556ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.610861ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (11633.757353ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (38168.11999ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (367.150641ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (145.663175ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (456.48183ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (316.350227ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (392.363184ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (115.172256ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (242.361847ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (305.279805ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (867.981978ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (411.049411ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (107.290437ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (79.610014ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (5.001315ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.182592ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (2.108134ms)
|
||||
✖ no shim from this file's tests is left running for the after hook (5069.458005ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3088.297138ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (471.125148ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2756.987349ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5023.712968ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2554.275667ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2735.958459ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2522.163222ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5637.374272ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (825.150571ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (925.855507ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (799.307491ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (1542.680606ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (552.967583ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (546.085838ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3285.771371ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (24.867ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (376.020519ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (349.317488ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (61.467027ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.090499ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (199.663758ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (304.621318ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (209.329704ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (270.026975ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (189.943314ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.423509ms)
|
||||
✔ 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.105029ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (284.880872ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (192.41889ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (153.892727ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (189.945342ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (194.689916ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.931016ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (196.780721ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (213.861585ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (237.075778ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (208.338445ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.442794ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.300169ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.32973ms)
|
||||
✔ 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.511474ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.938535ms)
|
||||
✔ 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.47667ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.817998ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.431762ms)
|
||||
✔ 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.365261ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.121626ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (229.95575ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (244.144747ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (419.408382ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (768.597537ms)
|
||||
✔ H4: self-takeover is refused (178.373354ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (389.61586ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (975.461449ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (248.747123ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (665.043422ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (310.63687ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (194.775379ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (191.465442ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (547.019679ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (290.665609ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (189.887032ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (933.090386ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (523.920195ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (981.477342ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (218.106746ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (351.898454ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.598431ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (694.089088ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (514.270031ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (939.240048ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3072.076081ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (653.420449ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.494042ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.54234ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.548775ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.498421ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.194536ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.447354ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.786317ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.744188ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.141692ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.091082ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (3.959532ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.701716ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.696632ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.324616ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.794386ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.567147ms)
|
||||
✔ F9: registrations never add or redirect a root (1.458632ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.704001ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.647529ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.690779ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.971159ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.435078ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.776037ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (509.154104ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.140956ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.694451ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.573043ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.247235ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.171709ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (758.079284ms)
|
||||
✔ the engine pin holds for the installed package (1.62462ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (247.368358ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (542.398677ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (238.906024ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (592.557974ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (408.954193ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (218.537145ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.789466ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (327.144677ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (187.305082ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (277.516328ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (443.666689ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1846.380562ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (194.887956ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (280.229511ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (184.022735ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (176.418657ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (397.733238ms)
|
||||
✔ N10: fake conformance (31.9138ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (473.51531ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (281.37194ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (327.373894ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (519.646809ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (519.289096ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (413.910759ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (216.468282ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (319.436286ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (673.393295ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (486.237526ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (671.744395ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (313.768091ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (182.117391ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (259.568701ms)
|
||||
✔ 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 (270.08494ms)
|
||||
✔ 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 (254.988226ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 164
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 63864.593514
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/claim.test.mjs:716:1
|
||||
✖ no shim from this file's tests is left running for the after hook (5069.458005ms)
|
||||
AssertionError [ERR_ASSERTION]: a test left its shim running
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ {
|
||||
+ pid: 3447135,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-now13/chat03-49mRK9/f97/sock/shim-caf8d6f42417.sock',
|
||||
+ unit: 'mosaic-chat-caf8d6f4241771cc4dd83db5c'
|
||||
+ }
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/claim.test.mjs:717:10)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { pid: 3447135, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-now13/chat03-49mRK9/f97/sock/shim-caf8d6f42417.sock', unit: 'mosaic-chat-caf8d6f4241771cc4dd83db5c' } ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (207.271804ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (27.378503ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (17.734201ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (401.463097ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (424.149463ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (260.321182ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (216.303228ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (198.475187ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.530333ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9580.246552ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (32490.502812ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (265.204197ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (146.055374ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (608.687771ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (283.134728ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (348.792391ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (110.258855ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (168.831108ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (293.044336ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (1011.928942ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (192.216459ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (95.914112ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (70.729115ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.126228ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.074734ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.743051ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.734544ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2951.343801ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (430.822757ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2740.61561ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4802.817192ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2465.610533ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2710.911294ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2365.599726ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5208.374708ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (705.036427ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (556.452572ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (678.820054ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (675.592705ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (324.575793ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (516.070431ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3248.044103ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (22.751721ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (316.43844ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (283.795513ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.41128ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (27.163484ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (197.270317ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (196.984698ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (242.28257ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (264.905841ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (251.57943ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.428407ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (263.885551ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (305.700471ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (223.918645ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (195.860354ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (214.150476ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (204.109833ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.909702ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (204.12687ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (225.290366ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (250.520935ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (206.142267ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.41733ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.27429ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.37043ms)
|
||||
✔ 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.502339ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.943287ms)
|
||||
✔ 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.504745ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.717556ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.483407ms)
|
||||
✔ 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.329417ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.099683ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (245.471689ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (246.021579ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (335.482637ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (923.349311ms)
|
||||
✔ H4: self-takeover is refused (191.466141ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (456.147402ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1017.207863ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (236.676987ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (661.959995ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (296.013005ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (265.514914ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (216.459394ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (771.292898ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (367.682417ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (228.139489ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (891.312509ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (307.782218ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (778.911906ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (198.565122ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (341.100834ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.610067ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (633.054053ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (535.933625ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (661.804814ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2929.74862ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (669.043564ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (27.421304ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (5.900029ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.636176ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.750661ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.40655ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.804873ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.858031ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.844388ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.246411ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.458235ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.565903ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.341684ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (4.313517ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.109439ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.687318ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.005059ms)
|
||||
✔ F9: registrations never add or redirect a root (1.84741ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.18711ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.831652ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.297263ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.129671ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.696325ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.866729ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (519.860154ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.62088ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.530826ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.372473ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.106265ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.06523ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (774.932604ms)
|
||||
✔ the engine pin holds for the installed package (1.897167ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (278.377573ms)
|
||||
✔ 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) (523.405456ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (242.204143ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (519.439509ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (414.693165ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (312.944285ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.937563ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (401.944364ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (205.60076ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (303.758094ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (463.80476ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1950.729013ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (232.903168ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (363.906033ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (282.17414ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (265.639301ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (474.858611ms)
|
||||
✔ N10: fake conformance (32.677774ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (447.029703ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (286.460188ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (290.431139ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (366.969251ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (380.832064ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (354.439631ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (199.111062ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (272.807596ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (676.108776ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (541.840477ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (492.00608ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (229.057693ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (175.273367ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (193.822882ms)
|
||||
✔ 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 (216.01477ms)
|
||||
✔ 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 (187.571295ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 165
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 49363.024117
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# run.sh <mutant>: mutate, run the conversation suite, list and kill shims left under TMPDIR, restore from backups.
|
||||
m=$1
|
||||
export TMPDIR=~/darkwing-scratch/r47a/tmp/mut-$m DOCKER_HOST=unix:///nonexistent.sock
|
||||
mkdir -p $TMPDIR
|
||||
M=~/darkwing-scratch/r47a/mut; cd ~/darkwing-scratch/r47a/wt
|
||||
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
|
||||
@@ -0,0 +1,13 @@
|
||||
Mr rc 1 ℹ pass 164 ℹ fail 1 shims-left 0
|
||||
noprobe rc 1 ℹ pass 164 ℹ fail 1 shims-left 0
|
||||
now13 rc 1 ℹ pass 164 ℹ fail 1 shims-left 0
|
||||
afterleak rc 1 ℹ pass 164 ℹ fail 2 shims-left 1
|
||||
sweeponly rc 1 ℹ pass 164 ℹ fail 1 shims-left 0
|
||||
sweeppid rc 1 ℹ pass 164 ℹ fail 1 shims-left 0
|
||||
k19leak rc 1 ℹ pass 164 ℹ fail 1 shims-left 0
|
||||
h23noreap rc 0 ℹ pass 165 ℹ fail 0 shims-left 0
|
||||
reapnosweep rc 0 ℹ pass 165 ℹ fail 0 shims-left 0
|
||||
DONE
|
||||
manifest rc=0
|
||||
h22noreap rc 1 ℹ pass 164 ℹ fail 1 shims-left 0
|
||||
manifest rc=0 after h22noreap (run after DONE)
|
||||
@@ -0,0 +1,200 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (219.22162ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (23.765285ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.835106ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (677.055327ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (565.256821ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (257.310579ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (239.136175ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (218.373694ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.590114ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12029.61787ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (30878.301788ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (287.664713ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (155.736773ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (452.81341ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (315.312236ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (379.01671ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (118.606186ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (211.612835ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (279.515244ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (785.941592ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (199.611313ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (95.335199ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (77.381194ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.361384ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.142543ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.938508ms)
|
||||
✖ no shim from this file's tests is left running for the after hook (5019.603223ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3200.252717ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (529.274581ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2868.634419ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5110.98429ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2541.203565ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2475.547415ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2361.050725ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (6461.358245ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (713.270412ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (564.741213ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (483.075851ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (794.671937ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (426.842821ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (569.619555ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3261.74847ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (27.269794ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (531.307009ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (465.646205ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.674166ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (25.397912ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (229.034341ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (502.781612ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (216.97478ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (451.470365ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (222.111955ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.397399ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (227.363437ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (339.547516ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (242.128975ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (210.685516ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (242.777703ms)
|
||||
✔ 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 (344.665889ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (9.896061ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (252.050993ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (254.617339ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (277.942168ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (235.804509ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.412249ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.261896ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.323508ms)
|
||||
✔ 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.476089ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.706168ms)
|
||||
✔ 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.384241ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (4.691081ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.356412ms)
|
||||
✔ 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.323979ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.096363ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (225.795171ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (269.285437ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (611.712432ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (1040.161233ms)
|
||||
✔ H4: self-takeover is refused (223.766709ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (483.992838ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1331.586166ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (281.034529ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (721.030469ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (365.24181ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (232.315138ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (218.980022ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (686.025613ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (307.533843ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (232.615491ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (923.383092ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (539.323132ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (838.31859ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (229.489054ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (396.824559ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.653646ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (678.692377ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (625.288367ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (722.611686ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2839.482157ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (610.170391ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (24.228875ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.463565ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.693891ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.733322ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.531828ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.344865ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.071245ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.803497ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.251514ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.411618ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.329635ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.675201ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (6.025147ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.7227ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.835583ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.728641ms)
|
||||
✔ F9: registrations never add or redirect a root (1.742691ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.9573ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.759548ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.09653ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.228863ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.408581ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.897507ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (534.197443ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.583271ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.532858ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.342871ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.15181ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.03718ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (779.363124ms)
|
||||
✔ the engine pin holds for the installed package (1.849074ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (279.715201ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (547.522098ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (242.72555ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (812.948913ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (615.318151ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (266.66633ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.877037ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (412.712916ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (226.935176ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (332.816958ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (652.278512ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1943.118139ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (222.075483ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (324.648835ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (237.783638ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (264.056599ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (389.449033ms)
|
||||
✔ N10: fake conformance (32.857365ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (413.601451ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (310.062237ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (294.630345ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (484.253844ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (496.213776ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (381.020463ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (207.059927ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (440.374037ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (649.883294ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (651.981202ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (489.606583ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (139.447168ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (173.130328ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (184.996545ms)
|
||||
✔ 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.728707ms)
|
||||
✔ 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 (239.825509ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 164
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53766.897467
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/claim.test.mjs:715:1
|
||||
✖ no shim from this file's tests is left running for the after hook (5019.603223ms)
|
||||
AssertionError [ERR_ASSERTION]: a test left its shim running
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ {
|
||||
+ pid: 3491796,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-sweeponly/chat03-NvSlTB/f55/sock/shim-c57cf2b7dc43.sock',
|
||||
+ unit: 'mosaic-chat-c57cf2b7dc4384ee284c7c8ca'
|
||||
+ }
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/claim.test.mjs:716:10)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { pid: 3491796, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-sweeponly/chat03-NvSlTB/f55/sock/shim-c57cf2b7dc43.sock', unit: 'mosaic-chat-c57cf2b7dc4384ee284c7c8ca' } ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (212.307304ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.241548ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.690237ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (365.735842ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (476.656649ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (252.219246ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (203.752506ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (218.059827ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.827711ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12986.540113ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (29667.327918ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (263.096526ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (153.684222ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (438.623192ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (292.216779ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (354.209654ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (99.843498ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (199.922328ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (270.3889ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (785.105243ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (197.740901ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (88.807427ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (69.375455ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.533954ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.057677ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.755071ms)
|
||||
✖ no shim from this file's tests is left running for the after hook (5015.591033ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3012.37604ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (456.752613ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2853.357608ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5101.754986ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2510.116268ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2507.829556ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2385.107361ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5096.425544ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (748.270129ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (698.688142ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (461.090943ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (764.265641ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (368.273775ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (585.010913ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3303.227292ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (44.55575ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (340.095364ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (407.45662ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (65.922828ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (26.780264ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (203.806154ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (198.186853ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (199.091556ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (346.518529ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (190.366735ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.478363ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (196.877259ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (303.887167ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (250.230797ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (190.267876ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (202.246951ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (204.796413ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.997429ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (206.23248ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (215.165122ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (232.091292ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (197.742321ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.433064ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.264916ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.33873ms)
|
||||
✔ 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.482271ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.503364ms)
|
||||
✔ 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.508488ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.194845ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.502874ms)
|
||||
✔ 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.374229ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.141727ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (244.73443ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (251.027442ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (295.533573ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (874.357109ms)
|
||||
✔ H4: self-takeover is refused (230.948664ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (432.850202ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1003.554755ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (203.401943ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (713.216897ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (327.609666ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (187.90498ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (180.014369ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (617.363478ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (269.250448ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (200.985765ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (893.863913ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (361.590816ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (772.788917ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (191.089022ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (339.528421ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.617666ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (662.309994ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (1578.967408ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (841.811267ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2893.408865ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (609.408041ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (24.887141ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.826388ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.874992ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.088607ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.046621ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.89486ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (3.832698ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (2.351568ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (2.059196ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (5.549088ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.912965ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (6.192415ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.499ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.619517ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (12.775709ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.547278ms)
|
||||
✔ F9: registrations never add or redirect a root (2.661461ms)
|
||||
✔ F10: a header cwd naming another project is refused (5.443268ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.748576ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.475394ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.950489ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.66476ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.810283ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (568.348502ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.675357ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.769068ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.773876ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.250822ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.338683ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (878.978932ms)
|
||||
✔ the engine pin holds for the installed package (1.990805ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (299.382164ms)
|
||||
✔ 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) (600.196612ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (293.379255ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (478.752914ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (496.518825ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (229.675189ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.369485ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (345.76914ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (209.867289ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (310.103199ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (449.582349ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1873.348979ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (145.516554ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (281.565014ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (202.855278ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (238.854877ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (366.704339ms)
|
||||
✔ N10: fake conformance (31.268588ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (387.555351ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (254.221566ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (318.654843ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (392.533184ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (375.602357ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (370.59127ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (197.627792ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (353.031336ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (687.713946ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (406.944589ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (1522.272602ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (237.477898ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (209.176383ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (176.405994ms)
|
||||
✔ 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 (146.513631ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (221.956939ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 164
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 52878.90005
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at packages/conversation/tests/claim.test.mjs:715:1
|
||||
✖ no shim from this file's tests is left running for the after hook (5015.591033ms)
|
||||
AssertionError [ERR_ASSERTION]: a test left its shim running
|
||||
+ actual - expected
|
||||
|
||||
+ [
|
||||
+ {
|
||||
+ pid: 3524005,
|
||||
+ socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-sweeppid/chat03-LcZocr/f55/sock/shim-c0dce842c2ea.sock',
|
||||
+ unit: 'mosaic-chat-c0dce842c2ea498500eca8f92'
|
||||
+ }
|
||||
+ ]
|
||||
- []
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r47a/wt/packages/conversation/tests/claim.test.mjs:716:10)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: [ { pid: 3524005, socket: '/home/jwoltje/darkwing-scratch/r47a/tmp/mut-sweeppid/chat03-LcZocr/f55/sock/shim-c0dce842c2ea.sock', unit: 'mosaic-chat-c0dce842c2ea498500eca8f92' } ],
|
||||
expected: [],
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (748.083419ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (48.776016ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (27.06872ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (421.457932ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (402.390693ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (254.045709ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (244.400915ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (239.716607ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.325186ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (13356.104987ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (36068.863466ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (252.623185ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (148.149392ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (630.377836ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (286.783672ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (347.24802ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (102.897217ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (220.305866ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (418.974636ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (932.402989ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (204.140858ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (92.498337ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (78.471097ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.439765ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.197349ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.813056ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (25.234145ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3507.986737ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (488.26111ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2807.697949ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5077.47375ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2577.773206ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2709.866313ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2448.965499ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5820.759171ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (729.781747ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (645.129116ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (652.019615ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (789.185088ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (367.180176ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (806.040921ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3273.574724ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (24.586649ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (299.433157ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (299.231036ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (81.16899ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (25.931375ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (203.436663ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (773.840378ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (268.793695ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (331.406567ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (220.921848ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.427235ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (205.441943ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (310.116384ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (271.294599ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (201.090126ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (231.32039ms)
|
||||
✔ 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 (218.02235ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.602383ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (224.057707ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (236.338343ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (281.785722ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (228.267016ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.418138ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.280314ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.306187ms)
|
||||
✔ 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.48246ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (30.948332ms)
|
||||
✔ 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.436041ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.664433ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.323378ms)
|
||||
✔ 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.399489ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.102825ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (221.258318ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (836.995174ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (345.379527ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (912.356131ms)
|
||||
✔ H4: self-takeover is refused (234.984573ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (468.136324ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1141.024711ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (236.737014ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (1160.793929ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (317.507653ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (222.364545ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (215.785684ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (633.428602ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (290.244169ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (221.311078ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (920.773309ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (667.241115ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1249.039812ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (331.549187ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (461.162549ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.655085ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (753.02375ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (619.748198ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (791.966316ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2953.46659ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (821.845284ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (25.659792ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (8.997065ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.836337ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.869323ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.785801ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.514939ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.08467ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (1.109708ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.43512ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.918335ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.373201ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.010585ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.206126ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.340925ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.045792ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.879664ms)
|
||||
✔ F9: registrations never add or redirect a root (1.864646ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.700498ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.909364ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (6.844885ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.439724ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.604906ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.802607ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (522.527587ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.380359ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.847291ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.816787ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.358511ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.385576ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (863.558219ms)
|
||||
✔ the engine pin holds for the installed package (2.284265ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (262.803824ms)
|
||||
✔ 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) (537.60608ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (331.695583ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (1083.153523ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (535.341366ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (248.458287ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.767648ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (386.548344ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (250.152615ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (324.635181ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (507.783689ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1931.729518ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (221.397946ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (324.497019ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (148.971188ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (261.283024ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (441.385924ms)
|
||||
✔ N10: fake conformance (32.218012ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (429.582597ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (273.423146ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (303.722538ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (395.259681ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (417.929598ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (669.815407ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (341.540953ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (551.736315ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (1329.575149ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (456.102377ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (595.331647ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (285.686961ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (232.628891ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (187.264585ms)
|
||||
✔ 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 (212.475028ms)
|
||||
✔ 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 (229.935301ms)
|
||||
ℹ tests 165
|
||||
ℹ suites 0
|
||||
ℹ pass 165
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 59084.73944
|
||||
@@ -0,0 +1,31 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3737.296989ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3966.21881ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (4260.503082ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (273.228743ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (234.452668ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (76.516425ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2784.361067ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (149.074355ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.127044ms)
|
||||
✔ serve refuses a --business value that is not a business id (203.925382ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3486.138769ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1870.194644ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1117.230752ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (870.280067ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3625.064473ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53317.26966ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3752.067649ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22335.011066ms)
|
||||
✔ loopback host and board origin fail closed (6.498143ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (102.695568ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (94.391046ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1768.953308ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 22
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53693.094645
|
||||
@@ -0,0 +1,13 @@
|
||||
node-conversation exit=0 ℹ pass 165 ℹ fail 0 shims-left=0
|
||||
node-webui exit=0 ℹ pass 22 ℹ fail 0 shims-left=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
|
||||
shims-left-end=0
|
||||
DONE
|
||||
@@ -0,0 +1,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,55 @@
|
||||
Note: switching to '21e0f1a31c559dfe4031e6de635ffb11d1a34606'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
Not currently on any branch.
|
||||
nothing to commit, working tree clean
|
||||
Note: switching to '21e0f1a31c559dfe4031e6de635ffb11d1a34606'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,26 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 24 passed, 0 failed
|
||||
@@ -0,0 +1,70 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
|
||||
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
|
||||
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
|
||||
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
|
||||
OK real pi with protocol vault adds reserve_id to the git verbs
|
||||
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
|
||||
OK real pi refuses a git key on a read-only root (fail closed)
|
||||
OK real pi with the pilot flags (--no-tools) exposes no tool at all
|
||||
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 178)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
|
||||
discord suite: 66 passed, 0 failed
|
||||
@@ -0,0 +1,21 @@
|
||||
OK initial ordinary-file install
|
||||
OK installed tree matches canonical source
|
||||
OK installed tree has no symlinks
|
||||
OK check detects installation drift
|
||||
OK sync refuses to overwrite installation drift
|
||||
OK check detects an extra destination file
|
||||
OK check detects an extra destination directory
|
||||
OK check rejects a destination symlink
|
||||
OK sync accepts a canonical source update
|
||||
OK updated installation matches canonical source
|
||||
scripts/test-extension-package.sh: line 14: 3331064 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
OK sync succeeds after interruption recovery
|
||||
OK unlocked stale lock file does not block
|
||||
OK active lock refuses a concurrent sync
|
||||
OK source symlink fails closed
|
||||
OK nested second entrypoint fails closed
|
||||
|
||||
extension package selftest: 18 passed, 0 failed
|
||||
@@ -0,0 +1,53 @@
|
||||
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
|
||||
|
||||
OK syntax: scripts/foundation-inspect.mjs
|
||||
OK syntax: scripts/foundation/strict-json.mjs
|
||||
OK syntax: scripts/foundation/canonical.mjs
|
||||
OK syntax: scripts/foundation/resolve.mjs
|
||||
OK syntax: scripts/foundation/validate-record.mjs
|
||||
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
|
||||
OK syntax: scripts/foundation/canonical.test.mjs
|
||||
OK syntax: scripts/foundation/cli.test.mjs
|
||||
OK syntax: scripts/foundation/fixtures.test.mjs
|
||||
OK syntax: scripts/foundation/resolve.test.mjs
|
||||
OK syntax: scripts/foundation/strict-json.test.mjs
|
||||
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
|
||||
OK fixture generator runs
|
||||
OK checked-in fixtures/bundles equal a fresh generation
|
||||
OK checked-in fixtures/raw equal a fresh generation
|
||||
OK checked-in fixtures/index.json equal a fresh generation
|
||||
OK checked-in demo bundles equal a fresh generation
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test scripts/foundation/ (ℹ pass 80)
|
||||
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
|
||||
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
|
||||
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
|
||||
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
|
||||
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
|
||||
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
|
||||
OK oracle: zero schema-column disagreements with the pinned checker
|
||||
OK oracle: strict-only profile refusals are counted and asserted
|
||||
OK demo: permitted read preview exits 0 (exit 0)
|
||||
OK demo: permitted file.change preview exits 0 (exit 0)
|
||||
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
|
||||
OK demo: revoked registration is refused (exit 3) (exit 3)
|
||||
OK demo: message is not authority (exit 3) (exit 3)
|
||||
OK usage: no arguments exits 2 (exit 2)
|
||||
OK io: missing file exits 4 (exit 4)
|
||||
OK io: directory exits 4 (exit 4)
|
||||
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
|
||||
OK bound: oversize fixture exits 2 (exit 2)
|
||||
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
|
||||
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
|
||||
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
|
||||
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
|
||||
OK text output starts with the disclaimer
|
||||
OK json output is valid JSON with result allowed and exactly the charter §7 fields
|
||||
OK json golden matches byte-for-byte
|
||||
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
|
||||
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
|
||||
OK canary never printed (bundle run and credential-file run)
|
||||
OK a non-bundle JSON file is refused at the shape gate, not read into output
|
||||
OK no field of the non-bundle file is echoed
|
||||
|
||||
selftest: 44 passed, 0 failed
|
||||
@@ -0,0 +1,35 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 148
|
||||
ℹ pass 148
|
||||
ℹ fail 0
|
||||
OK node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/darkwing-scratch/r47a/wt) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
@@ -0,0 +1,16 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
OK status safe on empty state (exit 0)
|
||||
OK status created no pointer
|
||||
OK fault-injected activation refuses (exit 1)
|
||||
OK refused activation wrote no pointer
|
||||
OK refusal logged exactly once with valid fields
|
||||
OK healthy activation succeeds (exit 0)
|
||||
OK pointer written with valid fields
|
||||
OK repeat activation succeeds (log grows) (exit 0)
|
||||
OK log is append-only across activations
|
||||
OK rollback without previous refuses (exit 1)
|
||||
|
||||
selftest: 14 passed, 0 failed
|
||||
@@ -0,0 +1,7 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
skip state-machine cases (docker daemon unavailable)
|
||||
|
||||
selftest: 4 passed, 0 failed
|
||||
@@ -0,0 +1,33 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
skip adapter seam cases (docker daemon unavailable)
|
||||
skip workspace/capability cases (docker daemon unavailable)
|
||||
skip live task cases (docker unavailable)
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
FAIL user recall run succeeds (exit 1)
|
||||
FAIL recalled user name (response: )
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 26 passed, 2 failed
|
||||
@@ -0,0 +1,158 @@
|
||||
# Row 47, S5 follow-up (Mr flows test and the shim leak), round 1 review (Darkwing)
|
||||
|
||||
Issue #1533, request comment 27036, notes comment 27037, queue revs 277
|
||||
and 278 (`10351f6a`). Brief: `docs/plans/2026-10-10_s5-follow-up-and-hygiene.md`
|
||||
(blob `24ceb88c`), section "S5 follow-up". Base `21e0f1a3`. Candidate
|
||||
manifest sha256
|
||||
`0587e393746e5cc6bb31e06b94461661fc7679f6f5fa6a72835c60b286a6055f`,
|
||||
6 files: five test files in `packages/conversation/tests/` and Dewey's
|
||||
`evidence.md`. No `src/` change.
|
||||
|
||||
Verdict: **approve**, comment 27043. The new flows test is the shape of my row 40 note 1
|
||||
probe in both chunkings, and it's the only test Mr fails. The leak
|
||||
diagnosis checks out against the code: the shim ignores SIGTERM, nothing in
|
||||
`src/` sends `release`, and two claim tests left a force-stopped scope for
|
||||
an `after` hook whose kill results nobody checked. Each test now reaps its own
|
||||
scope, every scope-launching file ends with a check that names a leak, and
|
||||
the `after` hook fails the file if a shim survives the sweep. Every leak
|
||||
mutant I tried in claim, cohort and races is killed, and `afterleak`
|
||||
reproduces the round 2 leak with the hook catching it. Two notes below,
|
||||
neither blocking.
|
||||
|
||||
## Method
|
||||
|
||||
- A detached worktree at `21e0f1a3` with the six files copied from a
|
||||
snapshot of the canonical tree, then `sha256sum -c`: 6 OK. Checked again
|
||||
after the mutants: 6 OK (`r1/mut/manifest-after.txt`).
|
||||
- I read the diff in full, `src/shim.mjs`, `ScopeLauncher` and
|
||||
`forceStopCohort` in `src/cohort.mjs`, `#run` in `src/terminal.mjs`, and
|
||||
W5, W13, K19, H22 and H23.
|
||||
- 10 mutants (`r1/mut/mutate.py`, `run.sh`), each run on the whole
|
||||
conversation suite with its own TMPDIR. After each run the runner lists any
|
||||
shim left under that TMPDIR, SIGKILLs its `mosaic-chat-*` scope and the PID,
|
||||
and restores the files from backup copies.
|
||||
- No shim was running before the gate, after it, or after any mutant other
|
||||
than `afterleak` (killed by the runner, see below). After all runs
|
||||
`systemctl --user list-units --all 'mosaic-chat-*'` lists nothing.
|
||||
|
||||
Node is v26.8.1, `TMPDIR=~/darkwing-scratch/r47a/tmp`. `gate.sh` set
|
||||
`DOCKER_HOST=unix:///nonexistent.sock`.
|
||||
|
||||
## Suites
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| conversation | 165/0 |
|
||||
| webui | 22/0 |
|
||||
| test-auth | 15/0 |
|
||||
| test-conductor | 17/0 |
|
||||
| test-config | 24/0 |
|
||||
| test-discord | 66/0 |
|
||||
| test-extension-package | 18/0 |
|
||||
| test-foundation | 44/0 |
|
||||
| test-queue | 27/0 |
|
||||
| test-release | 4/0 without Docker, 14/0 with it (`test-release-docker.txt`) |
|
||||
| test-task | 26/2 |
|
||||
|
||||
The two `test-task` failures are "user recall run succeeds" and "recalled
|
||||
user name", the live worker check that needs Docker and a model call. They
|
||||
fail the same way in my row 41 gates. Dewey's run reports 98/0, so it ran
|
||||
the Docker cases; I didn't.
|
||||
|
||||
## Mr
|
||||
|
||||
The test types Ctrl-T, Ctrl-O inside the text held behind it, `hi` Enter,
|
||||
then `yo` Enter and Ctrl-C, once as three chunks and once as one. It
|
||||
asserts `sent` is `["hi"]`, then `held` is null, then `yo` goes through and
|
||||
Ctrl-C quits. That's my `probe/stacked-later.mjs` from row 40, plus the
|
||||
single-chunk case, where the hold and the stacked hold come from one
|
||||
`#parse`. Under Mr (`holds = false;` at `terminal.mjs:158`) the suite is
|
||||
164/1 and the one failure is this test (`r1/mut/Mr-conversation.txt`).
|
||||
|
||||
## The leak
|
||||
|
||||
Dewey's reading matches the code:
|
||||
|
||||
- `shim.mjs` ignores SIGTERM and SIGHUP and exits only on `release` once the
|
||||
engine cgroup is empty.
|
||||
- The only shim ops `src/` sends are `hello`, `term`, `events`, `freeze`,
|
||||
`members` and `kill` (`cohort.mjs:115-192`). So a force-stopped scope
|
||||
stays up with its shim until something SIGKILLs the unit.
|
||||
- W5's probe and W13 force-stop a scope and left it to the `after` hook,
|
||||
whose `systemctl --user kill` and `stop` results were never checked.
|
||||
|
||||
The evidence names f55, the W5 probe fixture, as Filbert's leaked shim. My
|
||||
`afterleak` run leaked the same fixture (`f55/sock/shim-cede1ee324e5.sock`).
|
||||
Dewey says what the journal can't show, which is why the `after`
|
||||
kill missed in Filbert's run. The fix doesn't depend on knowing: the tests
|
||||
reap right away, and the sweep kills by PID whatever `systemctl` does.
|
||||
|
||||
The new helpers read `/proc/<pid>/cmdline` for `node <...>/shim.mjs --socket
|
||||
<path under tmp>`, which is exactly `ScopeLauncher`'s argv
|
||||
(`cohort.mjs:105`). `tmp` is per test process, so each file sees only its
|
||||
own shims, and a parallel gate from another seat isn't touched. `noprobe`
|
||||
failing proves the detector finds a real shim.
|
||||
|
||||
K19's `finally` used to assert on `kill` before sending `release`, so a
|
||||
failed kill left the scope. It now sends `release` only after a good kill,
|
||||
SIGKILLs the scope if either fails, and asserts afterwards.
|
||||
|
||||
## Mutants
|
||||
|
||||
| Mutant | Change | Conversation | Result |
|
||||
|---|---|---|---|
|
||||
| Mr | `holds = false;` after re-parsing held text | 164/1 | killed by the new flows test |
|
||||
| noprobe | W5 doesn't reap its probe | 164/1 | killed: claim's "no shim ... left running" |
|
||||
| now13 | W13 doesn't reap its fixture | 164/1 | killed, same test |
|
||||
| afterleak | noprobe, and `after` doesn't reap or sweep | 164/2 | killed: the same test and claim's `after` hook; 1 shim left, killed by the runner |
|
||||
| sweeponly | noprobe, and `after` sweeps but doesn't reap | 164/1 | killed by the last test; the sweep alone cleans up, no shim left |
|
||||
| sweeppid | sweeponly, and `killShims` skips the scope kill | 164/1 | same; the PID kill alone is enough |
|
||||
| k19leak | K19's `finally` sends no kill and no release | 164/1 | killed: cohort's "no shim ... left running" |
|
||||
| h22noreap | H22 (force stop, then relaunch in a scope) doesn't reap | 164/1 | killed: races' "no shim ... left running" |
|
||||
| h23noreap | H23 doesn't reap | 165/0 | equivalent: H23 runs on `pgroup`, no shim |
|
||||
| reapnosweep | `reap()` without its `killShims(fx.base)` | 165/0 | survives, expected: it's a backstop for a missed `systemctl` |
|
||||
|
||||
`afterleak` is the round 2 leak on purpose. Its hook failure names the shim
|
||||
(`r1/mut/afterleak-conversation.txt:205`), and node:test marks the file
|
||||
failed:
|
||||
|
||||
```
|
||||
AssertionError [ERR_ASSERTION]: no shim outlives this file (#1533)
|
||||
+ pid: 3459119,
|
||||
+ socket: '.../tmp/mut-afterleak/chat03-lQNQDH/f55/sock/shim-cede1ee324e5.sock',
|
||||
+ unit: 'mosaic-chat-cede1ee324e5b263ef2671152'
|
||||
```
|
||||
|
||||
So all three scope-launching files have their final check killed by a leak
|
||||
mutant, which Dewey's table showed only for claim.
|
||||
|
||||
## Notes (not blocking)
|
||||
|
||||
1. `liveShims` takes the first cgroup segment ending in `.scope` as the
|
||||
shim's unit, and `killShims` SIGKILLs that whole unit. Today every shim
|
||||
comes from `ScopeLauncher`, so that's the `mosaic-chat-*` scope. A future
|
||||
test that spawned `shim.mjs` directly from a shell inside a terminal's
|
||||
`app-*.scope` would get that terminal's scope killed. Matching
|
||||
`/^mosaic-chat-.*\.scope$/`, which is what my runner does, costs one
|
||||
regex. Worth folding into the next change to `harness.mjs`.
|
||||
2. The brief asks for the Mr output in the packet. `evidence.md` has the
|
||||
table and the `afterleak` excerpt; the outputs stay in
|
||||
`~/dewey-scratch`. Mine are in `r1/mut/` if Sage wants them on file.
|
||||
|
||||
## Finding for Sage
|
||||
|
||||
I agree with Dewey's out-of-scope finding and the recommended follow-up. In
|
||||
a running controller every force stop leaves a `mosaic-chat-*.scope` with
|
||||
an idle node shim, until the unit is killed by hand or the user manager
|
||||
restarts. The fix is `release` after a proven force stop (and on close of a
|
||||
stopped binding), with a test that the unit is gone. That's `cohort.mjs` or
|
||||
`controller.mjs` work, outside this brief.
|
||||
|
||||
## Files
|
||||
|
||||
- `r1/candidate-manifest.sha256`, `r1/files.txt`: copies of Dewey's.
|
||||
- `r1/gate.sh`, `r1/out/`: suite runs, `summary.txt` with a shim count
|
||||
after each node suite and at the end.
|
||||
- `r1/mut/`: mutant definitions, runner, per-mutant output and shims-left
|
||||
lists, `summary.txt`, and the manifest check after the runs. `h22noreap`
|
||||
ran after the first batch, so its line follows `DONE`.
|
||||
Reference in New Issue
Block a user