docs(queue-48): darkwing round 1 review, approve

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-10 03:49:12 -05:00
co-authored by Claude Opus 5.5
parent 85fac90139
commit 1355592f2b
25 changed files with 913 additions and 0 deletions
@@ -0,0 +1,3 @@
3107d6a48dbbd7e269bd2b65cdab47f32a7f66f9394b99d5a6c806ec804131ab agents/dewey/work/queue-48/evidence.md
ed4c1d1bdbbc0f4e087aca97a6ce47f0987dffa724c838c4e084b0c1881705ae packages/queue/tests/commit.test.mjs
8b08f75f1350ca14fe4e3879c3f7df4043b1d1dcf6a0a1a9f403193ee0f94125 scripts/test-task.sh
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
# Row 48 gate: queue node suite, then every scripts/test-*.sh with Docker unreachable.
export TMPDIR=~/darkwing-scratch/r48a/tmp DOCKER_HOST=unix:///nonexistent.sock
cd ~/darkwing-scratch/r48a/wt
O=~/darkwing-scratch/r48a/out
: > $O/summary.txt
echo "start $(date -u +%FT%TZ)" >> $O/summary.txt
node --test packages/queue/tests/*.test.mjs > $O/node-queue.txt 2>&1; e=$?
echo "node-queue exit=$e $(grep -E '^ℹ (pass|fail)' $O/node-queue.txt | tr '\n' ' ')" >> $O/summary.txt
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)" >> $O/summary.txt
echo DONE >> $O/summary.txt
@@ -0,0 +1,17 @@
diff --git a/packages/queue/tests/commit.test.mjs b/packages/queue/tests/commit.test.mjs
index c254dce5..41ee7538 100644
--- a/packages/queue/tests/commit.test.mjs
+++ b/packages/queue/tests/commit.test.mjs
@@ -166,10 +166,10 @@ async function pausedCommit(t, form) {
const go = join(r.ctl, "editor-go");
const editor = join(r.ctl, "editor.sh");
writeFileSync(editor, `#!/bin/sh\n: > ${q(started)}\nwhile [ ! -e ${q(go)} ]; do sleep 0.05; done\necho "ordinary" > "$1"\n`, { mode: 0o755 });
- const child = spawn("git", ["-C", r.root, "commit", "-e", "-q", ...form], { env: { ...r.env, GIT_EDITOR: editor }, stdio: ["ignore", "pipe", "pipe"] });
+ const child = spawn("sh", ["-c", 'git "$@" 2>"$0"; s=$?; (sleep 0.5; cat "$0" >&2) & exit $s', join(r.ctl, "git-err"), "-C", r.root, "commit", "-e", "-q", ...form], { env: { ...r.env, GIT_EDITOR: editor }, stdio: ["ignore", "pipe", "pipe"] });
let childErr = "";
child.stderr.on("data", (d) => { childErr += d; });
- const exited = new Promise((resolve) => child.on("exit", resolve));
+ const exited = new Promise((resolve) => child.on("close", resolve));
for (let i = 0; i < 200 && !existsSync(started); i++) sleepMs(50);
assert.ok(existsSync(started), "the editor never started");
const locked = existsSync(join(r.gitDir, "index.lock"));
@@ -0,0 +1,18 @@
✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1411.374746ms)
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1612.695499ms)
ℹ 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 (1669.334822ms)
ℹ git commit -e -- src.txt: index.lock held during the editor
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1485.27192ms)
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1059.81047ms)
✔ F1: a shared-index change during the procedure is not committed (1265.621309ms)
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1259.89775ms)
✔ F1: a missing or a different hook refuses (870.937246ms)
ℹ tests 8
ℹ suites 0
ℹ pass 8
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 10712.952509
@@ -0,0 +1,13 @@
diff --git a/packages/queue/tests/commit.test.mjs b/packages/queue/tests/commit.test.mjs
index c254dce5..b101cebc 100644
--- a/packages/queue/tests/commit.test.mjs
+++ b/packages/queue/tests/commit.test.mjs
@@ -166,7 +166,7 @@ async function pausedCommit(t, form) {
const go = join(r.ctl, "editor-go");
const editor = join(r.ctl, "editor.sh");
writeFileSync(editor, `#!/bin/sh\n: > ${q(started)}\nwhile [ ! -e ${q(go)} ]; do sleep 0.05; done\necho "ordinary" > "$1"\n`, { mode: 0o755 });
- const child = spawn("git", ["-C", r.root, "commit", "-e", "-q", ...form], { env: { ...r.env, GIT_EDITOR: editor }, stdio: ["ignore", "pipe", "pipe"] });
+ const child = spawn("sh", ["-c", 'git "$@" 2>"$0"; s=$?; (sleep 0.5; cat "$0" >&2) & exit $s', join(r.ctl, "git-err"), "-C", r.root, "commit", "-e", "-q", ...form], { env: { ...r.env, GIT_EDITOR: editor }, stdio: ["ignore", "pipe", "pipe"] });
let childErr = "";
child.stderr.on("data", (d) => { childErr += d; });
const exited = new Promise((resolve) => child.on("exit", resolve));
@@ -0,0 +1,58 @@
✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1019.923821ms)
✖ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (987.757774ms)
ℹ 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 (1002.567379ms)
ℹ git commit -e -- src.txt: index.lock held during the editor
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1435.596039ms)
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1023.967614ms)
✔ F1: a shared-index change during the procedure is not committed (1197.76575ms)
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1265.30256ms)
✔ F1: a missing or a different hook refuses (1148.064136ms)
ℹ tests 8
ℹ suites 0
ℹ pass 6
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 9135.584908
✖ failing tests:
test at packages/queue/tests/commit.test.mjs:195:1
✖ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (987.757774ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /cannot lock ref 'HEAD': is at 86e954928bfbe30c3845659fcbc78e6b9a6dd75d but expected 8300cce534f153bba6f53613705e7913099548c1/. Input:
''
at pausedCommit (file:///home/jwoltje/darkwing-scratch/r48a/wt2/packages/queue/tests/commit.test.mjs:186:10)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r48a/wt2/packages/queue/tests/commit.test.mjs:196:3)
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: /cannot lock ref 'HEAD': is at 86e954928bfbe30c3845659fcbc78e6b9a6dd75d but expected 8300cce534f153bba6f53613705e7913099548c1/,
operator: 'match',
diff: 'simple'
}
test at packages/queue/tests/commit.test.mjs:199:1
✖ F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update (1002.567379ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /cannot lock ref 'HEAD': is at e80c11b19ec36f5363799f753548696214e105a7 but expected 53249eecfa65a35d8748e6b1e5b0353649f4b0e6/. Input:
''
at pausedCommit (file:///home/jwoltje/darkwing-scratch/r48a/wt2/packages/queue/tests/commit.test.mjs:186:10)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r48a/wt2/packages/queue/tests/commit.test.mjs:200:3)
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: /cannot lock ref 'HEAD': is at e80c11b19ec36f5363799f753548696214e105a7 but expected 53249eecfa65a35d8748e6b1e5b0353649f4b0e6/,
operator: 'match',
diff: 'simple'
}
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
# Fake docker for the row 48 review: "info" succeeds, everything else goes to
# the real client, which can't reach the daemon (DOCKER_HOST unreachable).
if [ "$1" = info ]; then echo "fake docker info"; exit 0; fi
exec /usr/bin/docker "$@"
@@ -0,0 +1,2 @@
packages/queue/tests/commit.test.mjs: OK
scripts/test-task.sh: OK
@@ -0,0 +1,19 @@
# mutate.py <id>: apply one named variant to commit.test.mjs (exact text, must match once). Run from the tree root.
import sys
F = "packages/queue/tests/commit.test.mjs"
SPAWN = 'spawn("git", ["-C", r.root, "commit", "-e", "-q", ...form]'
# git exits with its own status at once; a background subshell keeps the
# stderr pipe open and writes git's message 0.5 s later.
DELAYED = 'spawn("sh", ["-c", \'git "$@" 2>"$0"; s=$?; (sleep 0.5; cat "$0" >&2) & exit $s\', join(r.ctl, "git-err"), "-C", r.root, "commit", "-e", "-q", ...form]'
CLOSE = 'child.on("close", resolve)'
M = {
"delay-close": [(SPAWN, DELAYED)],
"delay-exit": [(SPAWN, DELAYED), (CLOSE, 'child.on("exit", resolve)')],
}
s = open(F).read()
for old, new in M[sys.argv[1]]:
n = s.count(old)
if n != 1: sys.exit(f"{sys.argv[1]}: {n} matches")
s = s.replace(old, new)
open(F, "w").write(s)
print(sys.argv[1], "applied")
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# Runs after the gate: commit.test variants, then test-task with the fake docker.
export TMPDIR=~/darkwing-scratch/r48a/tmp
R=~/darkwing-scratch/r48a; M=$R/mut; W=$R/wt2
: > $M/summary.txt
echo "start $(date -u +%FT%TZ)" >> $M/summary.txt
for v in delay-exit delay-close; do
(cd $W && git checkout -q -- packages/queue/tests/commit.test.mjs && cp $R/snap/packages/queue/tests/commit.test.mjs packages/queue/tests/ && python3 $M/mutate.py $v && git diff -- packages/queue/tests/commit.test.mjs > $M/$v.diff)
(cd $W && node --test --test-name-pattern="F1: a" packages/queue/tests/commit.test.mjs > $M/$v.txt 2>&1); e=$?
echo "$v exit=$e $(grep -E '^ℹ (pass|fail)' $M/$v.txt | tr '\n' ' ')" >> $M/summary.txt
done
(cd $W && cp $R/snap/packages/queue/tests/commit.test.mjs packages/queue/tests/)
# The candidate's paused-commit tests, unmodified, ten times in a row.
for i in $(seq 1 10); do
(cd $W && node --test --test-name-pattern="F1: a" packages/queue/tests/commit.test.mjs > $M/repeat-$i.txt 2>&1); e=$?
echo "repeat-$i exit=$e $(grep -E '^ℹ (pass|fail)' $M/repeat-$i.txt | tr '\n' ' ')" >> $M/summary.txt
done
# test-task with docker info answering yes and the daemon unreachable.
(cd $W && DOCKER_HOST=unix:///nonexistent.sock PATH=$R/fakebin:$PATH scripts/test-task.sh > $M/test-task-fakedocker.txt 2>&1); e=$?
echo "test-task-fakedocker exit=$e $(grep -E 'passed, [0-9]+ failed' $M/test-task-fakedocker.txt | tail -1)" >> $M/summary.txt
(cd $W && sha256sum -c $R/manifest-mine.sha256) > $M/manifest-after.txt 2>&1
echo "end $(date -u +%FT%TZ)" >> $M/summary.txt
echo DONE >> $M/summary.txt
@@ -0,0 +1,16 @@
start 2026-10-10T08:45:22Z
delay-exit exit=1 ℹ pass 6 ℹ fail 2
delay-close exit=0 ℹ pass 8 ℹ fail 0
repeat-1 exit=0 ℹ pass 8 ℹ fail 0
repeat-2 exit=0 ℹ pass 8 ℹ fail 0
repeat-3 exit=0 ℹ pass 8 ℹ fail 0
repeat-4 exit=0 ℹ pass 8 ℹ fail 0
repeat-5 exit=0 ℹ pass 8 ℹ fail 0
repeat-6 exit=0 ℹ pass 8 ℹ fail 0
repeat-7 exit=0 ℹ pass 8 ℹ fail 0
repeat-8 exit=0 ℹ pass 8 ℹ fail 0
repeat-9 exit=0 ℹ pass 8 ℹ fail 0
repeat-10 exit=0 ℹ pass 8 ℹ fail 0
test-task-fakedocker exit=1 selftest: 67 passed, 31 failed
end 2026-10-10T08:47:18Z
DONE
@@ -0,0 +1,134 @@
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)
FAIL mock adapter: gate passes on matching mock response (exit 1, expected 0)
OK mock adapter: expect-mismatch recorded (exit 1)
FAIL mismatch reason recorded
OK unknown adapter fails closed (exit 1)
FAIL mission task runs via mock adapter (exit 1, expected 0)
grep: /home/jwoltje/darkwing-scratch/r48a/tmp/tmp.HNVro0rxF9/data/system-prompt.md: No such file or directory
FAIL mission section injected into generated prompt
FAIL retry of mission run succeeds (exit 1, expected 0)
OK retriedFrom lineage recorded
grep: /home/jwoltje/darkwing-scratch/r48a/tmp/tmp.HNVro0rxF9/data/system-prompt.md: No such file or directory
FAIL mission section present after retry (relative path resolved)
OK retry of missing run exits 4 (exit 4)
OK skill install bundled ms-tools (exit 0)
OK installed to skills-available
OK double install refuses (exit 1)
OK activate enables skill (exit 0)
OK enabled dir populated
OK uninstall while enabled refuses (exit 1)
OK deactivate moves back to available (exit 0)
OK uninstall removes from available (exit 0)
FAIL seat with enabled skill launches (exit 1)
SEATCASE stderr:
failed to connect to the docker API at unix:///nonexistent.sock; check if the path is correct and if the daemon is running: dial unix /nonexistent.sock: connect: no such file or directory
FAIL skill path delivered to adapter
OK shipped researcher contract resolves (ceiling + network)
OK resolve-role refuses a non-role document (conductor policy) (exit 2)
OK resolve-role refuses name/filename mismatch (exit 2)
OK resolve-role refuses unknown network declaration (exit 2)
OK resolve-role refuses duplicate role tool (exit 2)
OK resolve-role refuses unsupported tool (exit 2)
OK resolve-role refuses missing contract file (exit 4)
OK version 1 role prints no contract line
OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer)
OK resolve-role accepts a minimal version 2 role (exit 0)
OK resolve-role refuses an action outside the vocabulary (exit 2)
OK resolve-role refuses a gated-only action in a role file (exit 2)
OK resolve-role refuses a Vikunja verb no role may hold (exit 2)
OK resolve-role refuses a version 2 role whose contract is missing (exit 2)
OK resolve-role refuses a contract that is a symbolic link (exit 2)
FAIL seat launches under role ceiling (exit 1)
ROLESEAT stderr:
agent: capability policy: role 'analyst' ceiling narrowed tools -> read,bash
failed to connect to the docker API at unix:///nonexistent.sock; check if the path is correct and if the daemon is running: dial unix /nonexistent.sock: connect: no such file or directory
FAIL role ceiling narrows seat tools (read,write,bash -> read,bash)
OK narrowing recorded loudly
OK missing role contract refuses launch (exit 2)
OK missing-contract refusal names the role
FAIL empty ceiling intersection -> tool-free seat
OK tool-free outcome recorded loudly
OK overridden agents dir without seat definition refuses (exit 4)
OK seat-resolution refusal names the seat
FAIL policy: mission only -> mission tools
FAIL policy: task only -> task tools
FAIL policy: both -> intersection (task narrowed)
FAIL policy: empty intersection -> tool-free
OK invalid mission capabilities rejected (exit 2)
mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls)
FAIL task run with user layer present (exit 1, expected 0)
grep: /home/jwoltje/darkwing-scratch/r48a/tmp/tmp.HNVro0rxF9/data/system-prompt.md: No such file or directory
FAIL user context dispatched into generated prompt
FAIL workspace+tools task runs via mock (exit 1, expected 0)
FAIL workspace path + tools delivered to adapter
OK persistent workspace created on host
FAIL plain task still runs (no workspace/tools) (exit 1, expected 0)
FAIL workspace var present but empty when absent
FAIL tools empty when absent
OK unknown tool exits 2 (exit 2)
OK workspace traversal exits 2 (exit 2)
FAIL live hello task succeeds with exact marker
--- latest run evidence: /home/jwoltje/darkwing-scratch/r48a/tmp/tmp.HNVro0rxF9/data/runs/r-20261010T084717Z-c421be ---
--- stderr.txt (tail) ---
OK result.json written in run dir
FAIL result.json contents are correct
FAIL wrong expectExact (exit 1, reason: 'exit-nonzero')
--- latest run evidence: /home/jwoltje/darkwing-scratch/r48a/tmp/tmp.HNVro0rxF9/data/runs/r-20261010T084718Z-fccf9d ---
--- stderr.txt (tail) ---
OK each run gets a distinct run dir (no clobber)
OK list shows both runs
FAIL fork base: teach succeeds (exit 1, expected 0)
FAIL fork child recalls ancestor context (exit 4, expected 0)
node:fs:621
return binding.readFileUtf8(path, stringToFlags(options.flag));
^
Error: ENOENT: no such file or directory, open '/home/jwoltje/darkwing-scratch/r48a/tmp/tmp.HNVro0rxF9/data/runs/r-20261010T084718Z-5a44e7/result.json'
at Object.readFileSync (node:fs:621:20)
at [eval]:1:34
at runScriptInThisContext (node:internal/vm:219:10)
at node:internal/process/execution:485:12
at [eval]-wrapper:6:24
at runScriptInContext (node:internal/process/execution:483:60)
at evalFunction (node:internal/process/execution:317:30)
at evalTypeScript (node:internal/process/execution:329:3)
at node:internal/main/eval_string:71:3 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/home/jwoltje/darkwing-scratch/r48a/tmp/tmp.HNVro0rxF9/data/runs/r-20261010T084718Z-5a44e7/result.json'
}
Node.js v26.8.1
FAIL forked child recalled ancestor code word
OK ancestor session untouched by fork
FAIL child session dir has its own branch file
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: 67 passed, 31 failed
@@ -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 (1531.501337ms)
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1161.077924ms)
ℹ 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 (1083.010591ms)
ℹ 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 (1215.184151ms)
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1184.065991ms)
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1128.47923ms)
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1342.058049ms)
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (947.181707ms)
✔ F1: a shared-index change during the procedure is not committed (1130.0633ms)
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1040.512442ms)
✔ F1: a missing or a different hook refuses (739.480406ms)
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1169.125226ms)
✔ F1: the canary refuses a hook that git would not run (661.843976ms)
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (935.428692ms)
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (898.575201ms)
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (936.249459ms)
✔ bootstrap: the archived tests and validator run outside any repository (868.280964ms)
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (949.350742ms)
✔ general: a queue write after the snapshot is not committed (1330.564723ms)
✔ general: a snapshot whose log does not extend the base refuses (1197.082601ms)
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (160.827838ms)
✔ general: environment overrides, a linked worktree and usage (671.473149ms)
✔ general: a queue path staged before the run refuses at step 1 (766.792514ms)
✔ general: HEAD's queue tests failing in the archive refuse (887.336513ms)
✔ genesis document serializes deterministically and replays (4.026047ms)
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.298581ms)
✔ a tampered result, receipt or viewSha fails replay (2.147875ms)
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.151213ms)
✔ add: defaults for an ordinary seat, privileged extras, refusals (4.593521ms)
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (7.483789ms)
✔ matrix: release, review round, changes requested and waiting-on-jason (17.27717ms)
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (10.978861ms)
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (20.602956ms)
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (3.172689ms)
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1553.614941ms)
✔ matrix: blocked keeps the claim and returns only to previousState (3.693869ms)
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.470748ms)
✔ field edits: who may change what (6.546166ms)
✔ set issues keeps a logged narrowing of closes (N10) (4.13696ms)
✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.952963ms)
✔ every accepted text renders to nine cells on every row (N8) (23.261139ms)
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.524363ms)
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.507788ms)
✔ replay holds every op id to the caller's rule (N11) (5.050325ms)
✔ note: owner, listed reviewer or privileged; empty clears (1.109607ms)
✔ assign moves the claim with the owner; done clears it (2.474914ms)
✔ render is byte-stable and escapes pipes (0.496042ms)
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.594203ms)
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (22.585954ms)
✔ canonical args make a retry's identity independent of list order (0.33659ms)
✔ manifests, headings and blob ids (0.538689ms)
✔ the migration map: one queue-map block, exact keys (0.656175ms)
✔ every call but `queue` reaches the seat CLI exactly as before A2 (670.022459ms)
✔ `queue` reaches the queue CLI with the rest of the arguments (213.8728ms)
✔ the pre-A2 fixture is the script A2 changed (1.513614ms)
✔ acquire publishes the record by link; release removes only its own lock (10.159169ms)
✔ a kill between the temp write and the link leaves no lock (67.158611ms)
✔ a short or failed temp write refuses and leaves no lock and no temp (9.122205ms)
✔ a link error other than EEXIST refuses (11.688567ms)
✔ an error after the link releases the lock: unreadable gate, failing temp stat (24.423932ms)
✔ a release that fails on a gate path is reported, never a stack trace (P1) (47.551358ms)
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10102.409604ms)
✔ two concurrent unlockers: the second refuses on the gate (40.058729ms)
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (14.018763ms)
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (11.026477ms)
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (11.233607ms)
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (12.211658ms)
✔ the same pid and start on a different boot is mismatch (1.345584ms)
✔ a foreign host is unknown whatever the local pid says; unlock refuses (69.410494ms)
✔ unreadable /proc: classification is unknown and acquire refuses (0.63259ms)
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.192671ms)
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (19.27061ms)
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (21.173216ms)
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (7.482332ms)
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (31.341033ms)
✔ the migration map validates and renders the golden genesis table (4.280454ms)
✔ the marked QUEUE.md holds every row and parked item between its markers (1.700032ms)
✔ map-check reports each kind of drift (4.957619ms)
✔ a request posts once as the requester; a retry sends nothing (1027.42552ms)
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (5056.627475ms)
✔ the pre-send checks: GET user must name the requester, under the deadline (1807.060537ms)
✔ the lead's request refuses a token for login sage (896.678358ms)
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (781.945755ms)
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2225.736661ms)
✔ a same-op retry after a kill sends nothing, even with a stale view (2988.433742ms)
✔ a held lock at the outcome exits 3 and names what the transport said (691.126594ms)
✔ late outcomes: after an abandon, and after a resolve with the same or another id (2263.964507ms)
✔ resolve checks the comment: issue, markers, round, candidate and author (1723.878652ms)
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (734.561187ms)
✔ validateRow checks a request round's shape, which every replayed entry must keep (532.640941ms)
✔ request, changes, a new candidate, approval: every round pinned; no review files (2188.598637ms)
✔ a row with no reviewers opens a round that posts nothing (1206.674425ms)
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1795.85522ms)
✔ semantics: v1 entries replay as before; review entries need v2 (709.091871ms)
✔ set reviewers refuses the row's owner (2026-09-28) (361.989356ms)
✔ the owner records no verdict, even as a listed reviewer (514.455279ms)
✔ a request comment over the length limit is not sent (601.634189ms)
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (983.019857ms)
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (4427.326493ms)
✔ genesis: refusals before anything is written (453.091743ms)
✔ genesis: the map must be committed, well formed, with committed briefs and seats (738.032363ms)
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (705.369058ms)
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (487.089577ms)
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (713.997434ms)
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (921.685533ms)
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (800.244923ms)
✔ a retried add returns the id it first allocated, after reassignment and after done (1084.80495ms)
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (819.846ms)
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1319.403243ms)
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (774.41141ms)
✔ add, set reviewers and assign refuse the row's owner as a reviewer (499.086804ms)
✔ the working-brief check: a changed working copy refuses the start and flags next (648.106951ms)
✔ next: resume first, then nothing for an idle seat; needs a seat (368.110729ms)
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (678.034104ms)
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1184.889423ms)
✔ a hand edit to queue.json refuses every verb, reads included (570.452253ms)
✔ verify and render --check leave bytes and mtimes unchanged (416.969308ms)
✔ render is byte-stable across runs and repositories (324.131877ms)
✔ snapshot and verify --snapshot (761.556178ms)
✔ usage errors exit 4 (503.602105ms)
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (401.229971ms)
✔ a rename failure: nothing visible, temp removed (204.432783ms)
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (337.21166ms)
✔ a directory fsync failure, then sync names the op (424.153558ms)
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (311.285092ms)
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (232.10849ms)
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (263.277107ms)
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (203.425485ms)
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (235.683469ms)
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (235.375ms)
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (150.82536ms)
✔ a view write that fails keeps the op and reports a stale view (211.642707ms)
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (690.824969ms)
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (724.641647ms)
✔ SIGKILL after the witness, before the view: the stale refusal names the op (700.191807ms)
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (728.279727ms)
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (318.332383ms)
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1158.650713ms)
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (347.20694ms)
✔ a header edit during a write: the op stands, the view write is skipped with a warning (195.357948ms)
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (209.29275ms)
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (222.653744ms)
✔ a writer paused before and after the witness rename: readers see a tail, then a match (207.50805ms)
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (589.973193ms)
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (584.936039ms)
✔ the platform check refuses other filesystems (190.20406ms)
✔ tmpfs passes only a test layer that allows it (N5) (236.81327ms)
✔ unlock keeps a multi-line lock record on stdout (P3) (217.332847ms)
ℹ tests 148
ℹ suites 0
ℹ pass 148
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 33598.350827
@@ -0,0 +1,13 @@
start 2026-10-10T08:43:13Z
node-queue exit=0 ℹ pass 148 ℹ fail 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=0 selftest: 26 passed, 0 failed
end 2026-10-10T08:45:19Z
DONE
@@ -0,0 +1,17 @@
OK status with missing harness credential exits 3 and still lists accounts
OK status reports harness credential (read-only) + mosaic accounts
OK api key material never reaches output
OK oauth token material never reaches output
OK unparseable credential file exits 2
OK symlinked credential file exits 4
OK env-side credential names reported
OK env var values never reach output
OK accounts without an accounts dir reports none and creates nothing
OK accounts lists files and marks the active one
OK loose account perms flagged in listing
OK agent --auth with missing account file refuses (exit 4)
OK agent --auth with non-0600 account file refuses
OK agent --auth with invalid account name refuses
OK auth.sh without valid config refuses
selftest: 15 passed, 0 failed
@@ -0,0 +1,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: 358493 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/r48a/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,32 @@
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
skip live recall pair: user recall run, recalled user name (docker daemon unavailable)
OK no agent identity on headless run
selftest: 26 passed, 0 failed
@@ -0,0 +1,118 @@
# Row 48, test hygiene, round 1 review (Darkwing)
Issue #1534, request comment 27097, notes comment 27098, queue revs
317-319. Brief: `docs/plans/2026-10-10_s5-follow-up-and-hygiene.md`,
section "Test hygiene: commit.test exit wait and test-task Docker skip".
Base `5e87c41f`, which differs from `2d935f59` only in `docs/plans/QUEUE.md`
and `docs/plans/queue.json`. Candidate manifest sha256
`285a4ec13d3b2a9419911e7b0cc3ffe3af99dd8e564dea69eace339319df5259`. It
lists three files: the two changed files and Dewey's `evidence.md`. All
three check OK in the canonical tree.
Verdict: **approve**. Both changes do what the brief's "What ships" asks.
Without Docker, the recall pair prints a skip and leaves both counts
alone. When `docker info` says yes and the run fails, both checks still
fail. Waiting on `close` fixes the stderr race, and I reproduced the race
the same way Dewey did. Nothing blocks. I have three small notes.
## Method
- I read the diff in full and the brief's section, then compared the new
guard and skip line against the three Docker guards already in
`test-task.sh` (lines 139, 395, 442). They use the same `docker info`
test, and the skip line follows the same `skip ... (docker daemon
unavailable)` form.
- Detached worktree at `5e87c41f`, with the two files copied from a
snapshot of the canonical tree, then `sha256sum -c`: 2 OK. A second
worktree for the variants. Both of its files still check OK after the
runs (`r1/mut/manifest-after.txt`).
- A fake `docker` (`r1/mut/fake-docker`) answers `info` with exit 0 and
passes every other call to the real client, with `DOCKER_HOST` pointing
at a socket that doesn't exist. The guard sees Docker, and
`docker compose run` in `mosaic-task.mjs` fails before any container
starts. That exercises the "Docker present, run fails" path with no
model call.
- Two variants of the paused-commit tests (`r1/mut/mutate.py`): git's
stderr delayed 0.5 s by a background subshell that holds the pipe,
waited on `exit` and then on `close`. Then the unchanged candidate tests
ten times in a row, to check that `close` never hangs.
Node v26.8.1, `TMPDIR=~/darkwing-scratch/r48a/tmp`, `gate.sh` with
`DOCKER_HOST=unix:///nonexistent.sock`, 08:43:13Z to 08:45:19Z. Variants
ran 08:45:22Z to 08:47:18Z, then `test-release` with Docker.
## Suites
| Suite | Result |
|---|---|
| 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, Docker unreachable | 26/0, four skip lines, the last the new recall-pair skip |
| test-task, fake `docker info`, daemon unreachable | 67/31, rc 1 (below) |
I didn't run test-task with real Docker. That run makes live model
calls, and Dewey's 98/0 and Sage's 98/0 on row 52's landing already cover
it. The fake-Docker run is the one that tests the new branch with Docker
present.
## The Docker skip
| Run | Recall pair | Total |
|---|---|---|
| base, no Docker (rows 51 and 52) | both fail | 26/2 |
| candidate, no Docker | `skip live recall pair: ...` | 26/0 |
| candidate, fake `docker info`, run fails | `FAIL user recall run succeeds (exit 1)`, `FAIL recalled user name (response: )` | 67/31, rc 1 |
The pass count is 26 both with the pair failing and with it skipped, so
the skip adds nothing to `PASS`. With the fake Docker, the totals add up to
98, the same number of checks as Dewey's real run. The 31 failures are
every Docker-gated check failing at `docker compose run`. The two recall
checks are among them, and no skip line is printed.
## exit versus close
| Variant | `--test-name-pattern="F1: a"` |
|---|---|
| stderr delayed, wait on `exit` | 6/2: both paused-commit tests, `childErr` is `''` against `/cannot lock ref 'HEAD': .../` |
| stderr delayed, wait on `close` | 8/0 |
| candidate unchanged, ten runs | 8/0 each |
`close` resolves with the same exit code as the first argument, so
`assert.notEqual(code, 0)` still means what it did. The editor script runs
no background process, so nothing outlives git while holding the pipe, and
`close` can't hang on it.
The other four `exit` waits in the queue tests (`write.test.mjs:368`,
`lock.test.mjs:33`, `:65`, `:151`) read no output after the wait. They
don't need the change, which matches the brief's scope.
## Notes (not blocking)
1. The brief's problem statement says "Docker and a model". The guard
checks Docker only. With Docker up and no model credentials, the pair
still fails, along with every other live block. That's what "What
ships" asks for, and it's consistent with the three guards above it. I
agree with it.
2. The guard asks `docker info`, but the run uses `docker compose run`. A
host with the daemon and no compose plugin would fail the pair rather
than skip it. The other three blocks share this, so it isn't this row's
problem.
3. Dewey's two follow-ups stand. The fake-Docker run shows the second one:
"no agent identity on headless run" printed `OK` after a run that never
started. The first one (`FR` picks the newest run record) showed an
empty response here, not another run's, because the fork child's run
failed too.
## Files
- `r1/candidate-manifest.sha256`: copy of Dewey's.
- `r1/gate.sh`, `r1/out/`: suite runs and `summary.txt`.
- `r1/mut/`: variant definitions, runner, fake `docker`, outputs,
`summary.txt`, and the manifest check after the runs.