16 KiB
Hostcheck item 5 and bundle verification (Gitea #1816, Vikunja 614)
Queue row pending from Sage. Owner Gladstone, reviewer Morgana, gate/landing Sage. Base: fetched origin/refactor 93694c5185d0a7f989d2fe78fb620e44599b744e; branch gladstone/hostcheck-item5. Scope: mos-1531 / ops-0256, sage-3738 and sage-3740.
Before and after
Ubuntu 24.04 dpkg 1.22.6ubuntu6.6 prints root mosaic 750 /usr/bin/bwrap.
Item 5 compared the literal 0750, falsely refusing a correctly configured host;
--enforce would unload the profile, close bwrap and record DRIFT.
The candidate accepts exactly four fields, root, mosaic, octal 0750, and $BWRAP.
0*750 represents all octal spellings numerically equal to 0750 without arithmetic
conversion/overflow. Horizontal whitespace separates fields; extra fields and
embedded newlines refuse. Owner, group and path remain exact. The stat comparison
and existing output strings remain unchanged. Only the item 5 comment also changes.
The two release-transport bundle checks previously ran outside any repository. Each now creates an empty bare scratch repository, verifies with git -C, and removes the scratch directory using an EXIT trap. A failure exits the shell before cloning or fetching. No other run-sheet text changes.
Tests and mutants
Only packages/cli/tests/case1-hostcheck.test.mjs changes among tests, by appending three new test() blocks; the existing passing 0750 fake and failing fake are intact. The pass test exercises 750, 0750 and 000750. The enforce test checks no unload, chown, chmod, DRIFT or hostcheck log for 750. Refusals independently cover 755, 0755, 4750, 75, 7500, 075, non-octal strings, other owner/group/path, missing path, extra fields, multiple lines, blank/empty output and a missing fixture file.
| Mutant | Change | Killer | Result |
|---|---|---|---|
| M1 | Restore the old literal root mosaic 0750 comparison (drop 750) | new check pass test | Unrun |
| M2 | Accept any mode while preserving owner/group/path | new refusal test | Unrun |
Mutant procedure: preserve script bytes, assert one replacement, run the named killer alone, require failure for its intended assertion, restore in finally and check the original bytes/hash. Run the original targeted file again after restore.
Verification status
Untested. Sage-3738 prohibits all Node until CLEAR because the joint full gate runs alone. Sage-3740 explicitly directs pushing this untested build for Morgana's static review before CLEAR. This supersedes test-first execution for this draft; no Node, tests, node --check, full suites or live-host operations have run. Queue next refused linked-worktree execution; Sage owns queue creation/movement. Push only gladstone/*, with MOSAIC_GITEA_CREDENTIAL_FILE unset; no amend/force.
Static draft receipt: git diff --check passed. Test-file numstat against the
base is 46 0 packages/cli/tests/case1-hostcheck.test.mjs (added/deleted).
Scope and implementation amendment (mos-1534, sage-3741)
F1 correction: Sage requires precisely 750 or 0750 and exactly single spaces,
so the candidate now uses 0?750 and literal spaces. It refuses 00750/000750,
a doubled space and a tab. This supersedes the initial numeric-equivalence design
above. Only this row's own appended tests change; no base test line changes.
F3: case2-unit.sh plain() adds @. abs_plain is its only caller and requires an absolute normalized path, so @ cannot become an ExecStart prefix. In sed @ is ordinary replacement text (the delimiter is |); &, backslash and | still refuse. Environment=MOSAIC_CLAUDE_BIN carries @ literally; the parser compares the complete value, with no shell evaluation. @anthropic-ai also cannot match the residual @[A-Z_]+@ token check. Tests render/reinstall with a regular executable under @anthropic-ai, checking Environment and the new copy's WorkingDirectory. Since a nonroot test cannot create root ownership, a stat fake models root:0755 only for that file and its ancestor chain. The real file type/executable checks, render, installed-unit parser, and all other stat calls remain active. A separate block refuses space, %, $, both quote characters, backslash, & and |.
F4: copy_tree changes to / before archive/npm/find, after all its path arguments are absolute. copy_bundle retains its existing relative-bundle resolution before calling copy_tree. Checkout copies have absolute REPO and COPIES too. Source-only use from stack-release.sh changes no cwd because copy_tree is not called there. Audit of every AS_USER command in stack-upgrade.sh: fetch and merge use git -C; plan, gate, revert, confirm-rollback, stage (activate/rollback), confirm and rehearse invoke stack-release.sh, which changes to its checkout at line 34. No other AS_USER command needs a fix. The new test enters a directory mode 0111, verifies that ls cannot read it, then runs both a relative-bundle copy and checkout copy, asserting success, no find restore error, and an immutable completed copy.
Additional mutants (unrun under the same no-Node hold):
| Mutant | Change | Killer | Result |
|---|---|---|---|
| M3 | Drop @ from plain() | new factory scoped Claude-path test | Unrun |
| M4 | Remove copy_tree's cd / | new unreadable-cwd copy test | Unrun |
All four fixes remain untested. Follow-up is an additive commit on the pushed
branch, per sage-3741, with no amend or force. Morgana's static F1/F2 read of
f30c74b12 was CLEAN (morgana-0205); F3/F4 and execution remain open.
Morgana N1: the outer bundle-verification failure now prints
STOP: bundle verify failed; do not continue instead of exiting the interactive
root shell. The subshell still returns failure and cleans up. This follows the
sheet's human read-and-stop convention and avoids sending remaining pasted lines
to a parent shell after exiting sudo -i. Inner mktemp failure still exits only the
subshell. This supersedes the initial outer-shell failure behavior noted above.
File numstat against 93694c518 (added / deleted)
| File | Added | Deleted |
|---|---|---|
BUILD-LOG.md |
4 | 0 |
agents/gladstone/work/hostcheck-item5/BUILD.md |
114 | 0 |
docs/SESSIONS.md |
1 | 0 |
docs/guides/host-run-sheet.md |
12 | 2 |
packages/cli/tests/case1-hostcheck.test.mjs |
48 | 0 |
packages/cli/tests/case2-unit.test.mjs |
29 | 0 |
packages/cli/tests/stack-copy.test.mjs |
16 | 0 |
scripts/case1-hostcheck.sh |
2 | 2 |
scripts/case2-unit.sh |
1 | 1 |
scripts/stack-copy.sh |
3 | 0 |
F5 (mos-1536 / ops-0260): explicit notifier decision
Before: no run-sheet step creates the required notifier decision, and precheck accepts a host whose bus will refuse missing notify.json. After: section 9a requires the mosaic user to create its business notify directory at 0700 and an explicit {"notifyVersion": 1, "binding": null} file at 0600 before section 10. The shell runs with cwd / and umask 077; noclobber refuses an existing decision. Named binding is explicitly a later per-host choice requiring Mos's go. case2-install.sh is unchanged and makes no silent default.
case2-precheck.sh's existing facts helper dynamically imports readNotifyConfig from cli.mjs and calls it directly. File type, uid, exact 0600 mode, JSON keys, version and binding-name validation therefore stay identical to the bus. The shell checks the business notify directory with existing private_dir (same uid, not a symlink, exactly 0700) and prints the new notifier config line. No notifier is started and no configuration is written. Tests append pass cases for null and a valid named binding, and refusal cases for missing file, 0644, 0755 directory, extra key, malformed JSON, invalid binding/version and a symlinked file.
M5 (unrun): remove the notifier shell check, including its refusal branch; the new notifier refusal test must fail. Also include Morgana's F3 negative mutant M3b (allow $ in plain), killed by the new unsafe-character test.
Existing positive precheck tests need an explicit notify fixture. Approval requested via Sage in gladstone-0006/-0007 for minimal setup-only additions to case2-unit.test.mjs host(t) and case2-install.test.mjs world(t); assertions remain unchanged. Those additions are not yet applied. Correction to gladstone-0007: case1-accept.test.mjs also names case2-precheck.sh, but uses a stand-in; it does not execute the changed script and needs no fixture amendment.
Execution remains held by Sage: no Node/tests run. Morgana-0207 static F1-F4
verdict on a75532db1 is CLEAN; execution and F5 review are still open.
F5 acceptance from mos-1536 (for review):
- Before section 10, as mosaic, explicitly create /notify/ at 0700 and notify.json at 0600 with default binding null; a later named binding needs Mos's go. Do not make case2-install.sh write it.
- Precheck must refuse missing file, non-0600 file, directory looser than 0700 and a document outside the CLI's exact shape. Reuse/match cli.mjs validation, and name the new line in this run-sheet step's Precheck entry.
- Append-only test blocks cover pass, missing, 0644, 0755 dir, extra key; record a skip-check mutant, the touched test file and its numstat.
- F3/F4 precede F5, each follow-up commit stays on gladstone/hostcheck-item5; send pushes to Sage/Morgana with Mos cc. No Node until Sage CLEAR.
Static F5 draft receipt: git diff --check passed. Existing fixture approvals remain pending; F5's new pass test creates its own explicit decisions. No claim that the old positive precheck tests pass without the pending setup changes.
F5 candidate file numstat against 93694c518 (added / deleted)
| File | Added | Deleted |
|---|---|---|
BUILD-LOG.md |
6 | 0 |
agents/gladstone/work/hostcheck-item5/BUILD.md |
179 | 0 |
docs/SESSIONS.md |
1 | 0 |
docs/guides/host-run-sheet.md |
34 | 2 |
packages/cli/tests/case1-hostcheck.test.mjs |
48 | 0 |
packages/cli/tests/case2-unit.test.mjs |
65 | 0 |
packages/cli/tests/stack-copy.test.mjs |
16 | 0 |
scripts/case1-hostcheck.sh |
2 | 2 |
scripts/case2-precheck.sh |
19 | 0 |
scripts/case2-unit.sh |
1 | 1 |
scripts/stack-copy.sh |
3 | 0 |
Fixture approval and separate follow-up (mos-1537)
Mos approved exactly setup-only additions to case2-unit.test.mjs host(t) and
case2-install.test.mjs world(t): each gets three added lines for a 0700
notify/acme directory and 0600 notify.json with {"notifyVersion":1,"binding":null}.
No existing line, fake or assertion changes or is deleted. F5's missing-file
negative case removes the file after setup, so the new default cannot mask it.
The newly appended install test checks preserved bytes and mode after success,
and separately removes the decision before install and checks it stays absent.
case1-accept.test.mjs is unchanged. This follow-up is committed separately on
33950cf9b as directed, with no amend. No Node/tests run; suite claims remain open.
Morgana-0211 found F5 source/run sheet CLEAN statically; the setup prerequisite B1
is addressed by this approved fixture patch, pending execution.
Additional planned mutants from Morgana-0211 (unrun): M5b drops private_dir, killed by the directory fault; M5c prints NOTIFY ok unconditionally, killed by the document faults. Existing test preservation is checked by git diff showing only additions (numstat below).
Fixture follow-up numstat: case2-unit.test.mjs +3/-0, case2-install.test.mjs +18/-0. Against the original base: case1-hostcheck.test.mjs +48/-0, case2-unit.test.mjs +68/-0, stack-copy.test.mjs +16/-0, case2-install.test.mjs +18/-0. git diff --check passed.
F6 and notify parent correction (mos-1537 / ops-0261)
Before: install creates a copy but no human CLI wrapper; the sheet's stack-copy.sh check --units external therefore fails even with correct units. After: the sheet explicitly places a wrapper step after successful install and before that check. On Mos's separate live-step go, mosaic changes cwd to /, creates $HOME/.npm-global/bin if absent, then invokes its clone's stack-copy.sh wrapper --create. Existing wrappers refuse and require review; install is unchanged. This is documentation of a gated host action, not execution of it. There are no tests pinning these sheet steps to adjust.
Section 9a now explicitly creates/chmods /notify and then its business leaf, each 0700. This addresses ops-0261's observation that mkdir -p -m only controls the leaf. The existing umask 077 remains. All exits are inside the unprivileged shell, so no failure exits the operator's interactive root shell.
Static receipt: git diff --check passed. No Node, tests or live action. F6 and
this parent-directory documentation correction are a separate commit after the
approved fixture-only 5fd2328b6 follow-up. Reviewer should retain the earlier
F1-F5 source verdicts and inspect this delta. Queue row still pending from Sage.
F6 candidate file numstat against 93694c518 (added / deleted)
| File | Added | Deleted |
|---|---|---|
BUILD-LOG.md |
10 | 0 |
agents/gladstone/work/hostcheck-item5/BUILD.md |
241 | 0 |
docs/SESSIONS.md |
1 | 0 |
docs/guides/host-run-sheet.md |
51 | 2 |
packages/cli/tests/case1-hostcheck.test.mjs |
48 | 0 |
packages/cli/tests/case2-install.test.mjs |
18 | 0 |
packages/cli/tests/case2-unit.test.mjs |
68 | 0 |
packages/cli/tests/stack-copy.test.mjs |
16 | 0 |
scripts/case1-hostcheck.sh |
2 | 2 |
scripts/case2-precheck.sh |
19 | 0 |
scripts/case2-unit.sh |
1 | 1 |
scripts/stack-copy.sh |
3 | 0 |
F6 gate citation (sage-3747)
Added scripts/stack-copy.sh:3 to the F6 wrapper step's explicit Mos-go requirement. This is doc-only; commands and tests are unchanged. Sage ruled the delete-after- setup missing-file negative meets mos-1537, so no fixture change is needed. Morgana-0213 found the fixture/F6 deltas CLEAN statically. No Node/tests run; Morgana's eventual run waits for Sage's row and CLEAR after the 220+216 landing. Numstat for this follow-up: host-run-sheet.md +1/-1; BUILD.md +9/-0. Static receipt: git diff --check passed.
Correction: 0f3d874d4 added 10 BUILD.md lines, not 9; the git numstat is authoritative.
First review run and F4 test correction (morgana-0217)
Morgana ran af201b4a9 (6fa47e07d only relocates this file) under her authorized
slot with timeout -k 10 and TMPDIR under /mnt/storage/scratch/tmp. Receipts:
/mnt/storage/scratch/morgana-g320-out/base-summary.txt and mutants-summary.txt.
| Targeted file | Passed | Failed |
|---|---|---|
| case1-hostcheck.test.mjs | 19 | 0 |
| case2-unit.test.mjs | 47 | 0 |
| case2-install.test.mjs | 24 | 0 |
| stack-copy.test.mjs | 17 | 1 |
Seven of eight mutants were killed: M1 literal0750, M2 anymode, M3a drop@, M3b allow$, M5 drop shell block, M5b drop private_dir, M5c always NOTIFY ok. M4 drop cd / SURVIVED: base and mutant both failed only setup's cleanup hook. This is a test defect, not a green acceptance result. The 0111 cwd retained search permission and did not reproduce the inaccessible inherited cwd. It also blocked setup's chmod -R cleanup because the directory remained unreadable.
B1 correction touches only this row's appended F4 test: the child shell enters an initially searchable directory, chmods it 000, checks both ! -r and ! -x, then execs stack-copy. The parent asserts all read/search bits remain cleared. The bundle now lives outside cwd and is passed absolutely, matching the real flow; existing row232 coverage still tests relative bundles from an accessible cwd. Both bundle and checkout paths remain covered. B2 correction: each child run/assertion sequence has a finally that restores the cwd to0700 before setup's existing cleanup. No old setup/test lines change. Production scripts are unchanged; the cd / fix remains correct by static review.
Correction is untested locally: no builder Node/test CLEAR was given. Morgana will rerun stack-copy whole, M4 and residue cleanup on the pushed correction; other files need no rerun unless changed. git diff --check passed.
F4 corrected candidate numstat against 93694c518 (added / deleted)
| File | Added | Deleted |
|---|---|---|
BUILD-LOG.md |
12 | 0 |
agents/gladstone/work/queue-320/BUILD.md |
304 | 0 |
docs/SESSIONS.md |
1 | 0 |
docs/guides/host-run-sheet.md |
51 | 2 |
packages/cli/tests/case1-hostcheck.test.mjs |
48 | 0 |
packages/cli/tests/case2-install.test.mjs |
18 | 0 |
packages/cli/tests/case2-unit.test.mjs |
68 | 0 |
packages/cli/tests/stack-copy.test.mjs |
22 | 0 |
scripts/case1-hostcheck.sh |
2 | 2 |
scripts/case2-precheck.sh |
19 | 0 |
scripts/case2-unit.sh |
1 | 1 |
scripts/stack-copy.sh |
3 | 0 |