Darkwing'sef8b4439e(darkwing/row245), Duckworth round 1 APPROVE (rev 2849, comment 30043), manifest 69bc05f4f706, 5 paths. Slot run sage-1961 (darkwing-0107): singles 23/0, mutants 16/16 killed, N1 and N3 among them. DESIGN.md:336-338 stays unedited, superseded by gosalyn-0087. Sage gate on856c1826bplus these 5 paths (12:24-12:35Z): case1-accept.test.mjs alone 23/0, cli 681/0, every scripts/test-*.sh 429/0.
10 KiB
Row 195 build: scripts/case1-accept.sh
Row 195 (#1693), the Case 1 install on 10.1.1.53. Design r7 at 5425ea2f3, approved by Duckworth
(duckworth-0087, agents/duckworth/work/row195/REVIEW-r7.md at 58d930f89; Gitea 29962). Sage's go to build
is sage-1817.
What this commit holds
scripts/case1-accept.sh(new, 100755): section 5's acceptance, F1-F11 and P1-P11.packages/cli/tests/case1-accept.test.mjs(new): section 5's cases.docs/TOOLS.md: thecase1-accept.shentry after the hostcheck entry.- This file.
DESIGN.md, doc-only fixes Sage named in sage-1817, with no new design round:- Duckworth's N1. Section 1's human-id row cited
broker.mjs:245; the check isbroker.mjs:321. - Row 226's state at :21 and :326. It said "unclaimed"; 226 is assigned to Gosalyn, briefed and not started (sage-1814).
- :340 said I'd send the section 6 items to Gosalyn once 226 was claimed. I sent them on 2026-10-21 and Gosalyn acknowledged (gosalyn-0085), so the line now says that.
- Duckworth's N1. Section 1's human-id row cited
Nothing else changes. No case2-*.sh, stack-upgrade.sh, stack-release.sh, case1-hostcheck.sh or run
sheet edit, and nothing touches .53 (sage-1809).
Deviations from section 5, and choices it left open
- P4's mark is the single token
accepted Q182+Q183. Section 5 listsaccepted Q182andaccepted Q183as marks and gives P4 both. One line can carry one mark, and P4 is the only Q182 path, so the count list hasaccepted Q182+Q183in place ofaccepted Q182. - The test seam replaces
getent. Under--root, the script readsDIR/etc/passwdandDIR/etc/groupwith awk. Without it, the same helper callsgetent. The groups of a user are its primary gid, then each group whose member list names it, which is whatid -Gprints. So there is no fakeid, and the script never callsidfor another user. - P10 reads the port floor from
/proc/sys/net/ipv4/ip_unprivileged_port_startrather than callingsysctl, so the seam covers it the way hostcheck's item 7 is covered. There is no fakesysctl. - There is no fake
bwrap,claude,nodeorhostcheck.shon PATH. The script runs each by the absolute path the args file or the install names, so the test puts stubs at those paths under--root, and the fakesudoruns the stack's user's commands from the tree. - The args file must say
--profile factoryand name--business. Both are new refusals beyondstack-upgrade.sh's rules. A library file, or one for another business, would make F4 and F7 check the wrong thing. After--,--portand--tester-portmust be numeric. The args file is the only source of the ports. - F8, F9 and F11 read file metadata as root:
stat,-Landfind -maxdepth 1 -name '*.json'. None opens the credential. F9 parsesmanaged-settings.jsonwith the pinned node, run by root: that node and that file are both root's own, installed by ops-01. - F7 reads
launch.byfrom$HOME/.config/mosaic-dev/businesses/<id>.jsonas the stack's user. That path isbusinessFilePath(id)under the default config, which is what the unit uses. It doesn't load the copy'sbusiness.mjsasstack-release.sh'splugins launchbydoes. The user runs the whole check and prints one line, and root accepts onlyok <instance> run <run>(sage-1653: root doesn't parse the user's output beyond a fixed pattern). - F5 names a process claude when its argv0's basename or its
commstarts withclaude. The window confirms this against a real role session. If the bwrap'd claude shows another name, F5 fails closed and the fix is this match, not the label check. - F3 and F5 run together.
talkgoes out in the background undertimeout 330, and the/procscan repeats every half second until it finds a labelled pair ortalkexits. F3's line prints first, then F4, then F5, so the output keeps id order. F3 passes on exit 0 with thesentline and a reply line, both counted on stdout only (cli.mjs:395and theformatMessagewrite); talk's error goes to stderr. --claudemust match^/[A-Za-z0-9/._+@-]+$. The@is fornode_modules/@anthropic-ai/...;stack-upgrade.shdoesn't check--claudeat all.- F1 requires all four uids in
/proc/<MainPID>/status(real, effective, saved, filesystem) to be the user's. - Evidence text the stack's user controls (talk's last line, the precheck's last line, F6's and F10's
--version) is cut to printable ASCII, one line, 200 bytes.
Tests
packages/cli/tests/case1-accept.test.mjs, 22 tests. Every case builds a scratch host with every F check
passing and every P check closed or accepted, then changes one thing:
- all pass: exit 0, ids in order, each P mark, the count lines match the check lines, and the stand-in credential's value appears nowhere;
- each F check failing alone exits 1 with only its line failing;
- F3 on exit 0 with only the
sentline, and its evidence taking talk's stderr; - F1 on a failed unit and on
User=root; F2 on healthz failing and on another pid holding the port; - F5 on
unpriv_bwrap (enforce),bwrap//&unpriv_bwrap (complain), a trailing space on either label, no bwrap of the user's, and passing oncommalone; - F6 on a stub that prints nothing and on a path outside
claude-<pin>; - F7 on two live PM launches, a live one for another business, and no
launch.by; - F8 on 0640, on another owner and on a symlink, never printing the credential;
- F9 on a group-writable mode, another owner and a number for the flag;
- F11 on one
*.jsonunderdiscord/, and passing on an empty directory; - each P check's unnamed mark, with the line count each fault makes: P6 one line per member, P9 one per missing item; P2, P5, P10 and P11 in more than one way;
- every P check unnamed at once still exits 0;
- nine bad args files refuse with exit 2 before any command runs, each with its own refusal text;
- usage errors, and a user's run without
--root, exit 2; - every in-instance call goes through
sudo -u mosaic -H env PATH=<node dir>:/usr/local/bin:/usr/bin:/bin, read from the fakesudo's argv, and root's othersudocalls are exactly-l -U mosaic,-l -U localadminand the jarvis bwrap probe; - the host check records
--checkand never--enforce.
Mutants
Section 5's twelve, each one exact replacement in scripts/case1-accept.sh, run by
/mnt/storage/scratch/darkwing-195-out/mutants.py in Sage's slot (sage-1865). All twelve were killed.
After each one the script's bytes were restored, and the final sha256 matched (d434f15ff638...). The
killers below are the tests that failed in each mutant's saved log.
| # | Mutant | Killed by |
|---|---|---|
| 1 | F1 passes on a MainPID owned by root | each F alone |
| 2 | F2 accepts a listener on 0.0.0.0 | each F alone |
| 3 | F3 passes without a reply | each F alone; F3 exit 0 with no reply line |
| 4 | F5 matches a substring | F5 labels (the trailing space) |
| 5 | F6 skips the --version check |
each F alone; F6 stub and path |
| 6 | F8 skips the owner check | F8 0640, owner and symlink |
| 7 | F11 ignores discord/ |
each F alone; F11 *.json |
| 8 | P1 closes on any output | P unnamed; every P unnamed |
| 9 | P5 maps to closed with jarvis in docker |
P unnamed; every P unnamed; P2 and P5 names docker |
| 10 | P9 drops the jarvis item | P unnamed; P9 per item |
| 11 | P11 accepts any Before= |
P unnamed; every P unnamed |
| 12 | a P check's unnamed sets the exit to 1 |
P unnamed; every P unnamed; P10 and P11; P2 and P5; P9 per item |
Mutant 1 survived the F1 variants test (a failed unit, User=root), because those faults fail F1 on the
unit's state and User=, not on the uid line. Each F alone covers the uid line.
Pre-slot read review
Since every test run is slot work, a read-only subagent traced each case through the script before the first slot run, executing nothing. It found four defects, all fixed in this commit:
- The
--clauderegex had no@, so every run refused the args file, the real .53 one included. - The fake
statlogged the args-file call, so the bad-args test's "no command ran" check would fail. - The P5 and P6 faults each rewrote
etc/groupfrom the constant, so applying both lost P5's change. - F3 counted stderr into its line count, so a mutant dropping the exit check survived. F3 now counts
stdout only, and a
sent-only case covers the line count.
Run environment
The build commit ddf7dce6c sits on base 4ced5e63d (origin/refactor when it was pushed). It was first built
on c515273db as 4a51d392c. Row 238 then changed docs/TOOLS.md, so I cherry-picked the three commits onto
4ced5e63d as darkwing/row195-b. git range-diff shows all three identical. node v26.8.1 on dragon-lin,
every run behind my busy check and Sage's, chained with &&.
- Step 1, sage-1829 (07:41:54Z to 07:42:05Z), TMPDIR
/tmp/dw195:node --test packages/cli/tests/case1-accept.test.mjs, 22 pass, 0 fail, 11.0 s. - Step 2, sage-1865 (08:34:27Z to 08:36:21Z), TMPDIR
/mnt/storage/scratch/tmp/r195: the twelve mutants above, all killed. - Step 3, the gate (08:36:21Z to 08:47:06Z), same TMPDIR,
COMPOSE_PROJECT_NAME=darkwing-r195:
| Suite | Result |
|---|---|
packages/cli (node --test tests/*.test.mjs) |
662 pass, 0 fail, 0 skipped |
| test-auth.sh | 15 passed, 0 failed |
| test-conductor.sh | 17 passed, 0 failed |
| test-config.sh | 31 passed, 0 failed |
| test-discord.sh | 78 passed, 0 failed |
| test-extension-package.sh | 18 passed, 0 failed |
| test-foundation.sh | 44 passed, 0 failed |
| test-land-work.sh | 86 passed, 0 failed |
| test-queue.sh | 156 node pass, 0 fail; 28 passed, 0 failed |
| test-release.sh | 14 passed, 0 failed |
| test-task.sh | 98 passed, 0 failed |
Summed: 818 node tests passed with 0 failed, and 429 selftests passed with 0 failed. The compose network was removed afterwards, and the sweep found no process left in the worktree, the output directory or the TMPDIR. This file's results are a doc-only commit on top of the build, so nothing that ran has changed.
Correction to deviation 6 (follow-up, 2026-10-21)
Deviation 6 says the node F9 runs is root's own. At 37171f65b the script didn't check that. F9 ran
--node as root before F10's path check, so the header's "root itself only reads" was false
(Duckworth's N1, agents/duckworth/work/row195/REVIEW-m1.md at 6afcee2a5). A host installed as designed
was safe, because case2-unit.sh refused a non-root-owned --node. Row 245 carries the fix and its
tests: agents/darkwing/work/queue-245/BUILD.md.