From d4dd41db2bd8d2e6f179ba3905a1daa1411a91a1 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Tue, 13 Oct 2026 09:17:21 -0500 Subject: [PATCH] dewey: row 91 round 3 review packet, approve (comment 28150, #1585) Co-Authored-By: Claude Opus 5.5 --- .../dewey/work/queue-91-review/r3/mutants.sh | 105 ++++++++++++++++++ .../dewey/work/queue-91-review/r3/mutants.txt | 41 +++++++ agents/dewey/work/queue-91-review/r3/probe.sh | 22 ++++ .../dewey/work/queue-91-review/r3/probe.txt | 5 + agents/dewey/work/queue-91-review/r3/run.sh | 26 +++++ .../dewey/work/queue-91-review/r3/summary.txt | 23 ++++ .../dewey/work/queue-91-review/r3/verdict.md | 64 +++++++++++ 7 files changed, 286 insertions(+) create mode 100644 agents/dewey/work/queue-91-review/r3/mutants.sh create mode 100644 agents/dewey/work/queue-91-review/r3/mutants.txt create mode 100755 agents/dewey/work/queue-91-review/r3/probe.sh create mode 100644 agents/dewey/work/queue-91-review/r3/probe.txt create mode 100755 agents/dewey/work/queue-91-review/r3/run.sh create mode 100644 agents/dewey/work/queue-91-review/r3/summary.txt create mode 100644 agents/dewey/work/queue-91-review/r3/verdict.md diff --git a/agents/dewey/work/queue-91-review/r3/mutants.sh b/agents/dewey/work/queue-91-review/r3/mutants.sh new file mode 100644 index 000000000..f8a9d1577 --- /dev/null +++ b/agents/dewey/work/queue-91-review/r3/mutants.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +# Row 91 (#1585) round 3 mutants: round 1's list, rerun on the round 3 +# candidate, then the submodule guards. One or more per guard in +# checkout.mjs, then the launcher's refusal and record, then the prompt line. Run from the +# candidate worktree's root: +# LOG=/some/dir bash mutants.sh /path/to/mut.mjs > mutants.txt +# Each mutant edits one or two sites with mut.mjs (each exactly one match, +# else exit 3), +# runs the named tests and restores the file. "killed" means the run +# failed; "survived" means it passed. +set -u +MUT=$1 +run() { + local name=$1 file=$2 from=$3 to=$4; shift 4 + edits "$name" "$file" "$@" -- "$from" "$to" +} +# run2 name file from1 to1 from2 to2 tests...: two edits in one mutant. +run2() { + local name=$1 file=$2 a=$3 b=$4 c=$5 d=$6; shift 6 + edits "$name" "$file" "$@" -- "$a" "$b" "$c" "$d" +} +# edits name file tests... -- from to [from to] +edits() { + local name=$1 file=$2; shift 2 + local tests=() + while [ "$1" != "--" ]; do tests+=("$1"); shift; done; shift + local pairs=("$@") + cp -- "$file" "$file.orig" + if ! node "$MUT" "$file" "${pairs[@]}"; then + echo "$name: NO MATCH"; mv -- "$file.orig" "$file"; return + fi + if timeout -s KILL 400 node --test "${tests[@]}" > "${LOG:-.}/mut-$name.log" 2>&1; then + echo "$name: survived" + else + echo "$name: killed ($(grep -m1 -o 'ℹ fail [0-9]*' "${LOG:-.}/mut-$name.log"); $(grep '^✖ ' "${LOG:-.}/mut-$name.log" | grep -v 'failing tests' | sort -u | sed 's/^✖ //; s/ ([0-9.]*ms)$//' | paste -sd '|'))" + fi + mv -- "$file.orig" "$file" +} +C=packages/seat/src/checkout.mjs +L=packages/cli/src/launcher.mjs +B=packages/harness/src/bundle.mjs +CT=(packages/seat/tests/checkout.test.mjs) +LT=(packages/cli/tests/launcher.test.mjs) +BT=(packages/harness/tests/bundle.test.mjs) + +# The walk of .git. +run walk-off $C 'if (walk(join(dest, ".git"))) return "not-a-clone";' 'if (false) return "not-a-clone";' "${CT[@]}" +run walk-no-nlink $C '} else if (!st.isFile() || st.nlink !== 1) return true;' '} else if (!st.isFile()) return true;' "${CT[@]}" +run walk-symlinks-pass $C '} else if (!st.isFile() || st.nlink' '} else if ((!st.isFile() && !st.isSymbolicLink()) || st.nlink' "${CT[@]}" +run clone-dir-symlink-ok $C 'return lstatSync(p).isDirectory();' 'return lstatSync(p).isDirectory() || lstatSync(p).isSymbolicLink();' "${CT[@]}" +run no-commondir $C 'for (const f of ["commondir", join("objects", "info", "alternates")])' 'for (const f of [join("objects", "info", "alternates")])' "${CT[@]}" +run no-alternates $C 'for (const f of ["commondir", join("objects", "info", "alternates")])' 'for (const f of ["commondir"])' "${CT[@]}" +# Hooks and the config file. +run hooks-ignored $C 'if (readdirSync(join(dest, ".git", "hooks")).length > 0) return "hooks-present";' 'if (false) return "hooks-present";' "${CT[@]}" +run config-cap-off $C 'st.size > CONFIG_MAX_BYTES' 'st.size > Infinity' "${CT[@]}" +# The allowlist. +run allow-any-key $C 'default: return false;' 'default: return true;' "${CT[@]}" +run origin-any-url $C 'case "remote.origin.url": return value === root;' 'case "remote.origin.url": return true;' "${CT[@]}" +run fetch-any $C 'case "remote.origin.fetch": return value === FETCH;' 'case "remote.origin.fetch": return true;' "${CT[@]}" +run branch-remote-any $C 'return branch[1] === "remote" ? value === "origin" :' 'return branch[1] === "remote" ? true :' "${CT[@]}" +run branch-merge-any $C ': /^refs\/heads\/./.test(value ?? "");' ': true;' "${CT[@]}" +run duplicate-key-ok $C 'if (seen.has(key) || !allowed(key, value, root))' 'if (!allowed(key, value, root))' "${CT[@]}" +run no-origin-ok $C 'if (!seen.has("remote.origin.url")) return "config-changed";' '' "${CT[@]}" +# The root, the update and the clone. +run ceiling-off $C 'const ceiling = { GIT_CEILING_DIRECTORIES: dirname(root) };' 'const ceiling = {};' "${CT[@]}" +run detached-root-clones $C 'if (!at.branch) throw new CheckoutError(project, `the root ${root} has no branch checked out`);' '' "${CT[@]}" +run other-branch-updates $C 'if (!head.ok || head.out.trim() !== at.branch) return' 'if (!head.ok) return' "${CT[@]}" +run dirty-updates $C 'if (status.out !== "") return { outcome: "kept", reason: "dirty" };' '' "${CT[@]}" +run untracked-ignored $C '"--untracked-files=all", ' '"--untracked-files=no", ' "${CT[@]}" +run merge-not-ff-only $C '"merge", "-q", "--ff-only", "--no-edit"' '"merge", "-q", "--no-edit"' "${CT[@]}" +run temp-left $C 'rmSync(tmp, { recursive: true, force: true });' 'void tmp;' "${CT[@]}" +run clone-in-place $C 'const tmp = join(workspace, `.${project}.clone-${randomBytes(4).toString("hex")}`);' 'const tmp = dest;' "${CT[@]}" +run clone-hardlinks $C '"clone", "-q", "--no-hardlinks", ' '"clone", "-q", ' "${CT[@]}" +# Second-line guards: the checks above stop each case first, so these are +# expected to survive. +run safe-no-hookspath $C '"-c", "core.hooksPath=/dev/null",' '' "${CT[@]}" +run safe-no-fsmonitor $C '"-c", "core.fsmonitor=false",' '' "${CT[@]}" +# The launcher. +run refusal-unmapped $L 'if (e instanceof CheckoutError) throw new Refusal(' 'if (false) throw new Refusal(' "${LT[@]}" +run kept-unrecorded $L 'for (const c of checkouts) if (c.outcome === "kept") record(' 'for (const c of checkouts) if (false) record(' "${LT[@]}" +run checkouts-not-in-session $L 'session: { run, launchedBy, workspace, toolSocket, turnMarker, checkouts },' 'session: { run, launchedBy, workspace, toolSocket, turnMarker },' "${LT[@]}" +# The prompt line. +run prompt-no-clones $B '...checkoutLines(session.checkouts ?? []),' '' "${BT[@]}" +run prompt-kept-as-current $B '(c.outcome === "kept" ?' '(c.outcome === "never" ?' "${BT[@]}" +# Round 3: the submodule guards. The first two must die; the next three are +# second-line guards the first two stop first, so they are expected to +# survive; the last two take the first away and must die on the marker. +run gitlink-check-off $C 'if (staged.out.split("\0").some((e) => e.startsWith("160000 "))) return { outcome: "kept", reason: "submodule" };' '' "${CT[@]}" +run modules-check-off $C 'return "submodule";' '' "${CT[@]}" +run status-recurses $C '"--ignore-submodules=all"' '"--ignore-submodules=none"' "${CT[@]}" +run safe-no-submodule-recurse $C '"-c", "submodule.recurse=false",' '' "${CT[@]}" +run lsfiles-failure-passes $C 'if (!staged.ok) return { outcome: "kept", reason: "status-failed" };' '' "${CT[@]}" +run2 recursion-unguarded $C 'if (staged.out.split("\0").some((e) => e.startsWith("160000 "))) return { outcome: "kept", reason: "submodule" };' '' '"--ignore-submodules=all"' '"--ignore-submodules=none"' "${CT[@]}" +run2 gitlink-check-after-status $C 'if (staged.out.split("\0").some((e) => e.startsWith("160000 "))) return { outcome: "kept", reason: "submodule" };' '' '"--ignore-submodules=all"], env); + if (!status.ok) return { outcome: "kept", reason: "status-failed" };' '"--ignore-submodules=none"], env); + if (!status.ok) return { outcome: "kept", reason: "status-failed" }; + if (staged.out.split("\0").some((e) => e.startsWith("160000 "))) return { outcome: "kept", reason: "submodule" };' "${CT[@]}" +# Dewey's additions (row 91 round 3 review): the clone's empty template, the +# clones before the run, and the fetch's own no-recursion flag (second line, +# expected to survive). +run clone-template-default $C '"clone", "-q", "--no-hardlinks", "--template=", ' '"clone", "-q", "--no-hardlinks", ' "${CT[@]}" +run2 checkouts-after-run $L ' const { run, runDir } = newRun(dataRoot, b.id); +' '' ' const workspace = workspaceDir(dataRoot, b.id, instance);' ' const { run, runDir } = newRun(dataRoot, b.id); + const workspace = workspaceDir(dataRoot, b.id, instance);' "${LT[@]}" +run fetch-recurses $C '"--no-recurse-submodules", ' '' "${CT[@]}" diff --git a/agents/dewey/work/queue-91-review/r3/mutants.txt b/agents/dewey/work/queue-91-review/r3/mutants.txt new file mode 100644 index 000000000..4d7cfd7e8 --- /dev/null +++ b/agents/dewey/work/queue-91-review/r3/mutants.txt @@ -0,0 +1,41 @@ +walk-off: killed (ℹ fail 7; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a reflog hard-linked to a file outside|config a symlink to /dev/zero|.git/logs a symlink|.git/objects a symlink|.git/refs/heads a symlink|HEAD a fifo) +walk-no-nlink: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a reflog hard-linked to a file outside) +walk-symlinks-pass: killed (ℹ fail 5; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|config a symlink to /dev/zero|.git/logs a symlink|.git/objects a symlink|.git/refs/heads a symlink) +clone-dir-symlink-ok: killed (ℹ fail 3; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|.git a symlink|the clone a symlink to the bait) +no-commondir: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a commondir file) +no-alternates: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|an alternates file) +hooks-ignored: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a hook file) +config-cap-off: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a config file over the size cap) +allow-any-key: killed (ℹ fail 7; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a filter driver|a gitlink and submodule.active in config|core.fsmonitor|core.hooksPath|includeIf|include.path) +origin-any-url: killed (ℹ fail 3; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|an ext:: origin|origin moved) +fetch-any: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a fetch line onto local branches) +branch-remote-any: killed (ℹ fail 2; a branch tracking another remote|a clone the session rigged is kept, and git runs nothing and writes nothing outside it) +branch-merge-any: killed (ℹ fail 2; a branch merging a tag|a clone the session rigged is kept, and git runs nothing and writes nothing outside it) +duplicate-key-ok: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|the origin url twice, both the root) +no-origin-ok: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|no origin) +ceiling-off: killed (ℹ fail 2; a plain directory inside another repository|a root that can't be cloned refuses with the project id and leaves nothing behind) +detached-root-clones: killed (ℹ fail 2; a root that can't be cloned refuses with the project id and leaves nothing behind|on no branch) +other-branch-updates: killed (ℹ fail 2; a dirty clone, one on another branch and one diverged are kept as they are|another branch) +dirty-updates: killed (ℹ fail 4; a dirty clone, one on another branch and one diverged are kept as they are|a modified file|an untracked file|a staged file) +untracked-ignored: killed (ℹ fail 2; a dirty clone, one on another branch and one diverged are kept as they are|an untracked file) +merge-not-ff-only: survived +temp-left: killed (ℹ fail 2; a root that can't be cloned refuses with the project id and leaves nothing behind|missing an object) +clone-in-place: killed (ℹ fail 1; the clone is not at its place until git clone has finished) +clone-hardlinks: killed (ℹ fail 47; a branch merging a tag|a branch tracking another remote|a clone ahead of the root keeps its commits and counts as current|a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a clone whose root is on no branch, or gone, is kept and named|a commondir file|a config file over the size cap|a detached HEAD|a dirty clone, one on another branch and one diverged are kept as they are|a diverged branch|a fetch line onto local branches|a filter driver|a first launch clones the root at its branch, with the root's path as origin|a gitlink and .gitmodules committed|a gitlink and .gitmodules staged|a gitlink and submodule.active in config|a gitlink staged, no .gitmodules|a gitlink to a repository with its own gitlink|a .git/modules with no gitlink|a hook file|a later launch fast-forwards a clean clone after a commit to the root, then finds it current|a modified file|an alternates file|an ext:: origin|another branch|an untracked file|a reflog hard-linked to a file outside|a second origin url|a staged file|config a symlink to /dev/zero|core.fsmonitor|core.hooksPath|.git a gitdir: file|.git a symlink|.git/logs a symlink|.git/objects a symlink|.git/refs/heads a symlink|HEAD a fifo|includeIf|include.path|no credential or forge remote of the root reaches a clone|no origin|origin moved|the clone a file|the clone a plain directory|the clone a symlink to the bait|the origin url twice, both the root) +safe-no-hookspath: survived +safe-no-fsmonitor: survived +refusal-unmapped: killed (ℹ fail 1; a project clone that can't be made refuses checkout-failed, from --pm and from a session, with no run and no clone left) +kept-unrecorded: killed (ℹ fail 1; each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or kept and logged) +checkouts-not-in-session: killed (ℹ fail 1; each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or kept and logged) +prompt-no-clones: killed (ℹ fail 1; the prompt names the project clones in the workspace, and none when there are none) +prompt-kept-as-current: killed (ℹ fail 1; the prompt names the project clones in the workspace, and none when there are none) +gitlink-check-off: killed (ℹ fail 5; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a gitlink and .gitmodules committed|a gitlink and .gitmodules staged|a gitlink staged, no .gitmodules|a gitlink to a repository with its own gitlink) +modules-check-off: killed (ℹ fail 2; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a .git/modules with no gitlink) +status-recurses: survived +safe-no-submodule-recurse: survived +lsfiles-failure-passes: survived +recursion-unguarded: killed (ℹ fail 5; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a gitlink and .gitmodules committed|a gitlink and .gitmodules staged|a gitlink staged, no .gitmodules|a gitlink to a repository with its own gitlink) +gitlink-check-after-status: killed (ℹ fail 5; a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a gitlink and .gitmodules committed|a gitlink and .gitmodules staged|a gitlink staged, no .gitmodules|a gitlink to a repository with its own gitlink) +clone-template-default: killed (ℹ fail 33; a branch merging a tag|a branch tracking another remote|a clone ahead of the root keeps its commits and counts as current|a clone the session rigged is kept, and git runs nothing and writes nothing outside it|a clone whose root is on no branch, or gone, is kept and named|a config file over the size cap|a detached HEAD|a dirty clone, one on another branch and one diverged are kept as they are|a diverged branch|a fetch line onto local branches|a filter driver|a first launch clones the root at its branch, with the root's path as origin|a gitlink and .gitmodules committed|a gitlink and .gitmodules staged|a gitlink and submodule.active in config|a gitlink staged, no .gitmodules|a gitlink to a repository with its own gitlink|a hook file|a later launch fast-forwards a clean clone after a commit to the root, then finds it current|a modified file|an ext:: origin|another branch|an untracked file|a second origin url|a staged file|core.fsmonitor|core.hooksPath|includeIf|include.path|no credential or forge remote of the root reaches a clone|no origin|origin moved|the origin url twice, both the root) +checkouts-after-run: killed (ℹ fail 1; a project clone that can't be made refuses checkout-failed, from --pm and from a session, with no run and no clone left) +fetch-recurses: survived diff --git a/agents/dewey/work/queue-91-review/r3/probe.sh b/agents/dewey/work/queue-91-review/r3/probe.sh new file mode 100755 index 000000000..1a63ef043 --- /dev/null +++ b/agents/dewey/work/queue-91-review/r3/probe.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Does git status run a submodule's clean filter with --ignore-submodules=all vs =none, +# under checkout.mjs's SAFE flags and environment? Builds the bait fresh per mode. +set -u +B=$HOME/dewey-scratch/r91probe/w +SAFE=(-c core.hooksPath=/dev/null -c core.fsmonitor=false -c protocol.allow=never -c protocol.file.allow=always -c submodule.recurse=false) +E=(env -i PATH="$PATH" HOME=/nonexistent GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null GIT_TERMINAL_PROMPT=0 LC_ALL=C) +tg(){ "${E[@]}" git -c user.name=t -c user.email=t@example.invalid "$@"; } +for nested in 0 1; do for mode in all none; do + rm -rf "$B"; mkdir -p "$B"; M=$B/RAN + printf '#!/bin/sh\ntouch %s\ncat\n' "$M" > $B/run.sh; chmod +x $B/run.sh + tg init -q -b main $B/c; echo one > $B/c/README; tg -C $B/c add README; tg -C $B/c commit -q -m one + mk(){ tg init -q -b main $1; echo one > $1/data; echo 'data filter=x' > $1/.gitattributes; tg -C $1 add data .gitattributes; tg -C $1 commit -q -m s; tg -C $1 config filter.x.clean $B/run.sh; echo ONE > $1/data; } + if [ $nested = 0 ]; then mk $B/c/sub; tg -C $B/c update-index --add --cacheinfo 160000,$(tg -C $B/c/sub rev-parse HEAD),sub + else tg init -q -b main $B/c/sub; echo x > $B/c/sub/f; tg -C $B/c/sub add f; mk $B/c/sub/sub + tg -C $B/c/sub update-index --add --cacheinfo 160000,$(tg -C $B/c/sub/sub rev-parse HEAD),sub; tg -C $B/c/sub commit -q -m o + tg -C $B/c update-index --add --cacheinfo 160000,$(tg -C $B/c/sub rev-parse HEAD),sub; fi + rm -f $M + out=$("${E[@]}" git "${SAFE[@]}" --no-optional-locks --git-dir=$B/c/.git --work-tree=$B/c status --porcelain=v1 -z --untracked-files=all --ignore-submodules=$mode | tr '\0' '|'); rc=$? + echo "nested=$nested ignore=$mode rc=$rc marker=$([ -e $M ] && echo PRESENT || echo absent) status='$out'" +done; done +rm -rf "$B" diff --git a/agents/dewey/work/queue-91-review/r3/probe.txt b/agents/dewey/work/queue-91-review/r3/probe.txt new file mode 100644 index 000000000..75c0324f0 --- /dev/null +++ b/agents/dewey/work/queue-91-review/r3/probe.txt @@ -0,0 +1,5 @@ +nested=0 ignore=all rc=0 marker=absent status='' +nested=0 ignore=none rc=0 marker=PRESENT status='AM sub|' +nested=1 ignore=all rc=0 marker=absent status='' +nested=1 ignore=none rc=0 marker=PRESENT status='AM sub|' +2026-10-13T13:18:10Z git version 2.55.0 diff --git a/agents/dewey/work/queue-91-review/r3/run.sh b/agents/dewey/work/queue-91-review/r3/run.sh new file mode 100755 index 000000000..a5c289b8f --- /dev/null +++ b/agents/dewey/work/queue-91-review/r3/run.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# Dewey's row 91 round 3 gate and mutants. Gate on ~/dewey-scratch/r91 (2b2a9e14 + build.patch staged), +# mutants on ~/dewey-scratch/r91mut. Only test-task gets the dead Docker socket. +set -u +O=~/dewey-scratch/r91-out; W=~/dewey-scratch/r91; M=~/dewey-scratch/r91mut +export TMPDIR=~/dewey-scratch/tmp +busy(){ for p in $(pgrep -f -- --test) $(pgrep -f "scripts/test-"); do [ "$p" = "$$" ] && continue; c=$(readlink /proc/$p/cwd 2>/dev/null) || continue; case "$(cat /proc/$p/comm 2>/dev/null)" in node*|bash) ;; *) continue;; esac; case "$c" in $HOME/dewey-scratch/*) ;; *) return 0;; esac; done; return 1; } +if busy; then echo "BUSY, abort" >> $O/summary.txt; exit 9; fi +cd $W || exit 2 +echo "start $(date -u +%Y-%m-%dT%H:%M:%SZ) base $(git rev-parse HEAD) staged=$(git diff --cached --name-only | wc -l)" >> $O/summary.txt +sha256sum -c --quiet $O/candidate-manifest.sha256 && echo "manifest 8/8 OK" >> $O/summary.txt || { echo "manifest mismatch" >> $O/summary.txt; exit 3; } +for p in tasks bus harness webui cli business seat control-board; do + case $p in seat|webui) g="packages/$p/tests/";; *) g="packages/$p/tests/*.test.mjs";; esac + node --test "$g" > $O/gate/node-$p.txt 2>&1; rc=$? + echo "node $p rc=$rc $(grep -E '^ℹ (tests|pass|fail|skipped)' $O/gate/node-$p.txt | tr '\n' ' ') $(grep -c '✖' $O/gate/node-$p.txt)x" >> $O/summary.txt +done +for f in scripts/test-*.sh; do n=$(basename $f .sh) + if [ $n = test-task ]; then DOCKER_HOST=unix:///nonexistent.sock bash $f > $O/gate/$n.txt 2>&1; else bash $f > $O/gate/$n.txt 2>&1; fi; rc=$? + echo "$n rc=$rc $(tail -1 $O/gate/$n.txt | cut -c1-100)" >> $O/summary.txt +done +echo "suites end $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $O/summary.txt +cd $M || exit 2 +sha256sum -c --quiet $O/candidate-manifest.sha256 || { echo "mut manifest mismatch" >> $O/summary.txt; exit 3; } +LOG=$O/mut bash $O/mutants.sh $O/mut.mjs > $O/mutants.txt 2>&1; echo "mutants rc=$? end $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $O/summary.txt +sha256sum -c --quiet $O/candidate-manifest.sha256 && git status --porcelain | grep -v '^M ' | grep -v '^A ' | wc -l | sed 's/^/mut copy manifest OK, stray entries: /' >> $O/summary.txt || echo "mut copy NOT restored" >> $O/summary.txt +echo "done $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $O/summary.txt diff --git a/agents/dewey/work/queue-91-review/r3/summary.txt b/agents/dewey/work/queue-91-review/r3/summary.txt new file mode 100644 index 000000000..6fb1a2314 --- /dev/null +++ b/agents/dewey/work/queue-91-review/r3/summary.txt @@ -0,0 +1,23 @@ +start 2026-10-13T14:08:39Z base 2b2a9e14eec4ff81cdada5dc42bbf1b8063b29b4 staged=8 +manifest 8/8 OK +node tasks rc=0 ℹ tests 89 ℹ pass 89 ℹ fail 0 ℹ skipped 0 0x +node bus rc=0 ℹ tests 115 ℹ pass 115 ℹ fail 0 ℹ skipped 0 0x +node harness rc=0 ℹ tests 67 ℹ pass 67 ℹ fail 0 ℹ skipped 0 0x +node webui rc=0 ℹ tests 104 ℹ pass 104 ℹ fail 0 ℹ skipped 0 0x +node cli rc=0 ℹ tests 131 ℹ pass 131 ℹ fail 0 ℹ skipped 0 0x +node business rc=0 ℹ tests 60 ℹ pass 60 ℹ fail 0 ℹ skipped 0 0x +node seat rc=0 ℹ tests 89 ℹ pass 89 ℹ fail 0 ℹ skipped 0 0x +node control-board rc=0 ℹ tests 124 ℹ pass 124 ℹ fail 0 ℹ skipped 0 0x +test-auth rc=0 selftest: 15 passed, 0 failed +test-conductor rc=0 selftest: 17 passed, 0 failed +test-config rc=0 selftest: 24 passed, 0 failed +test-discord rc=0 discord suite: 66 passed, 0 failed +test-extension-package rc=0 extension package selftest: 18 passed, 0 failed +test-foundation rc=0 selftest: 44 passed, 0 failed +test-queue rc=0 queue suite: 27 passed, 0 failed +test-release rc=0 selftest: 14 passed, 0 failed +test-task rc=0 selftest: 26 passed, 0 failed +suites end 2026-10-13T14:11:56Z +mutants rc=0 end 2026-10-13T14:15:22Z +mut copy manifest OK, stray entries: 1 +done 2026-10-13T14:15:22Z diff --git a/agents/dewey/work/queue-91-review/r3/verdict.md b/agents/dewey/work/queue-91-review/r3/verdict.md new file mode 100644 index 000000000..f6682edd3 --- /dev/null +++ b/agents/dewey/work/queue-91-review/r3/verdict.md @@ -0,0 +1,64 @@ +**Dewey: approve, round 3, candidate manifest `ca57060709379b89b5e4e81b2e13ad68ead2be14f7106ea38e6c416680b4f7df`** + +I'm the reviewer for this row under decision 130 (sandbox row, Claude review, no Codex check). This verdict covers the whole row on round 3's manifest, not only the round 3 delta. + +**What I checked** + +- `build.patch` from `agents/darkwing/work/queue-91/r3/`, applied with `git apply --index` to a detached worktree of `2b2a9e14`, checks 8/8 against `candidate-manifest.sha256`. No commit from `2b2a9e14` to `refactor` HEAD touches the eight paths. +- Static review of `packages/seat/src/checkout.mjs` in full, its tests, the launcher's `start()` change, the prompt line, and both READMEs. + - The submodule fix closes Filbert's escape from both sides. `unsafe()` keeps a clone with anything at `.git/modules` as `submodule`. `update()` reads the index with `ls-files --stage -z` before any other git runs in the clone, and keeps any gitlink (`160000`) as `submodule`. `status` uses `--ignore-submodules=all`, and `SAFE` adds `-c submodule.recurse=false`. `ls-files` runs only after the config allowlist passes, and it reads the index only. + - The six new cases build a real bait: a repository at `/sub` whose own config names a clean filter, with a same-size edit, so any status inside it must run the filter. Each case asserts the outcome, that the marker is absent, and that the bait repository is unchanged. + - The launcher makes the clones after `credentialStatus` and before `newRun`, so a `checkout-failed` leaves no run. A session launch refuses through `refuse()` and the launch log; `--pm` refuses through the log and exit 3, as the other steps do. A kept clone is a `checkout-kept` record and the launch goes on, as the brief says. + +**The assert-order point in your build.md** + +Your build.md notes that under `gitlink-check-off` the outcome assertion fails before the marker assertion, so the tests don't show whether `--ignore-submodules=all` alone stops the filter. I checked that directly: `probe.sh` here builds your bait (a staged gitlink to a repository whose own config names a clean filter, same-size edit; and the two-level form) and runs `git status` with `SAFE`'s flags and `gitEnv()`'s environment, git 2.55.0, 13:18:10Z: + +| bait | `--ignore-submodules=all` | `--ignore-submodules=none` | +|---|---|---| +| one level | marker absent, status empty | marker present, `AM sub` | +| two levels, filter in the inner repository only | marker absent, status empty | marker present, `AM sub` | + +So `=none` reproduces Filbert's escape on this git, and `=all` alone stops it at both depths. `status-recurses` surviving is a real second-line guard, not a dead one. No test change needed. + +**Gate** + +Run 14:08:39Z to 14:11:56Z on `2b2a9e14` plus the candidate (manifest 8/8 before and after). Every suite green. This is a full rerun: my first run died in the 13:40Z dragon-lin reboot, and I discarded its partial output. + +| suite | pass | suite | pass | +|---|---|---|---| +| tasks | 89/89 | auth | 15 | +| bus | 115/115 | conductor | 17 | +| harness | 67/67 | config | 24 | +| webui | 104/104 | discord | 66 | +| cli | 131/131 | extension-package | 18 | +| business | 60/60 | foundation | 44 | +| seat | 89/89 | queue | 27 | +| control-board | 124/124 | release | **14** | +| | | task (dead socket) | 26 | + +The counts you reported match mine, except test-release (see below). + +**Mutants** + +Your 38 under a 400 s KILL timeout, then 3 of mine (14:11:56Z to 14:15:22Z). The mutant copy checked 8/8 against the manifest afterwards. + +- **Your 38:** 32 killed, 6 survived, the same six you report: merge-not-ff-only, safe-no-hookspath, safe-no-fsmonitor, status-recurses, safe-no-submodule-recurse, lsfiles-failure-passes. I agree each one is equivalent or a second-line guard: + - **hooksPath and fsmonitor:** `unsafe()` already keeps a clone with a hook file or either key, and the global config is `/dev/null`. + - **submodule.recurse and status-recurses:** a gitlink or `.git/modules` is kept before `status` runs. The probe above shows `=all` alone holds even when that check is off. + - **lsfiles-failure-passes:** an `ls-files` that fails after the allowlist passes has no route to a recursing call that I can find. +- **Mine:** + - `clone-template-default` (drop `--template=`) is killed by 33 tests. + - `checkouts-after-run` (move `ensureCheckouts` after `newRun`) is killed by the "no run and no clone left" test. + - `fetch-recurses` (drop `--no-recurse-submodules`) survives, as I expected: a clone that gets as far as `fetch` has no gitlink in its index and no `.git/modules`, so nothing is populated for fetch to recurse into. It's the same kind of second guard as `submodule.recurse=false`. + +**My round 2 points** + +- test-release: your gate ran every script with `DOCKER_HOST` at a dead socket, so `test-release.sh` ran 4 of its 14 cases, the same mistake I made in my row 94 round 2 run. Mine gives the dead socket to `test-task.sh` only, and test-release ran all 14 cases, green. Sage's gate rerun is the one that counts; it should run test-release with Docker. +- merge-not-ff-only: equivalent on this host, as you say. Under `gitEnv()`, `git var GIT_COMMITTER_IDENT` exits 128 with "unable to auto-detect email address (got 'jwoltje@dragon-lin.(none)')", so a merge without `--ff-only` can't commit and the diverged clone is kept as `not-fast-forward` either way. On a host whose name has a domain, the "a diverged branch" case would kill it. Non-blocking. + +**Non-blocking notes** + +1. A project whose root has submodules is never updated after its first launch. `git clone` without `--recurse-submodules` leaves the gitlinks in the clone's index, so every later `update()` keeps the clone as `submodule`. That's the safe choice, and no project has one today: the only business file names `stack`, `/mnt/storage/src/mosaic-stack`, which has 0 gitlinks. The seat README's reasons table says what `submodule` means but not that a root with submodules always gets it. Worth one sentence there when someone next touches the README; it doesn't need another round. + +**Landing:** the launcher runs in the bus host, so the change is live at the next bus restart (decision 109). Mos hears before the landing. Nothing here is under `webui/src/public`, so decision 123 doesn't apply.