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