4.1 KiB
Brief: tests for the two proof-file paths row 187 left open
Sage, 2026-10-20, for mos-0956. P19 tasks 409 (M8) and 410 (M9), Filbert's N1 and N2 on row 187 (#1685
comment 29424). Owner Darkwing, reviewer Filbert, gate owner Sage. Base: refactor at 742e98976. Line numbers
are at that commit.
Mos's ruling: this row lands before the Part B window runs
(agents/sage/work/window-plan/repin-partB-proof.md). Part B is the first time a real proof file goes through
these checks, and that file holds a credential.
Why
Row 187's proof_checks (scripts/stack-release.sh:745) refuses a bad proof pair before anything stops. Two of
its paths have no test that fails when they break:
- M8: on a carried pair (the pointer's pair, with no pair flags given),
proof_for_target(:694) reaches theelsebranch and thenproof_checksat :739. Filbert's mutant makes that branch return before the checks. It survived T53 12/12. So a proof file that was moved, chmod-ed or replaced after the activate that set the pair would pass the plan and the precheck and fail only after the stop, inwebui-service.sh render(:194-201). - M9:
proof_file_problem(:765) refuses a path whose realpath is inside REPO or the copies (:775). T53 covers the copies (:1595) but not REPO. Filbert's mutant drops the two REPO cases. It survived.
webui-service.sh render still refuses both files after the stop, so neither mutant leaks the value today. The
gap is that the refusal comes after both units are down, which is what row 187 exists to prevent.
The change
Tests only, in packages/cli/tests/stack-release.test.mjs, as new subtests of T53 or a new T54. No change to
scripts/stack-release.sh, scripts/webui-service.sh, docs/TOOLS.md or packages/webui. The script's blob
must stay 2586a2a37cd2e7c7aeaba6d31490e5b7920fb4b2, since the Part B window file names it and Mos's A2 check
reads it. P19 407 and 408 (a comment at :689 and the row 178 paragraph in docs/TOOLS.md) are not in this row
for the same reason.
Tests
- A carried pair whose file changed since it was set. Set a pair with an activate, then for each of these
changes run
--planand the real activate to another copy with no pair flags, on a fresh stand-in each:- chmod the file to 644;
- replace it with a symlink to a mode-600 file;
- remove it;
- chown is out of reach in a test, so the uid case is not required.
Each plan lists
proof-file-invalidwith the reason, and each run exits 3 with no unit stop or start, no log line and the pointer unchanged.
- The same for a
rollbackthat carries the pair to a copy whose Console takes it. One case (chmod 644) is enough. - A proof file inside the test's REPO (
join(base,'repo'), :238), mode 600 and owned by the test's uid, given withactivate ... --proof-header NAME --proof-file PATH. The plan and the run both refuseproof-file-invalidwithis inside the repository or the copies, with no stop. - A path outside REPO that resolves inside it: a directory symlink outside REPO pointing into it, with the
proof file under it. The file itself is not a symlink, so the
-Lcheck passes and the realpath check must catch it. - Each new case runs
noLeak.
Base control: run the new tests against the mutants below, and on base (where they must pass). Record each result in BUILD.md.
Mutants, each one exact replacement in scripts/stack-release.sh, each killed by a new test:
- M8: in
proof_for_target,return 0at the end of the carried branch, beforeproof_checks. - M9: drop
"$REPO" | "$REPO"/* |from the realpath case at :775. - M10:
proof_checksskipsproof_file_problemwhen PROOF_HEADER is empty (the carried case only).
Put the script back after each mutant and check its sha256.
Gate
Filbert approves the candidate manifest. The cli suite and every scripts/test-*.sh pass on Sage's gate from a
git worktree of the candidate. The only touched files are packages/cli/tests/stack-release.test.mjs and the
packet. Name anything else in BUILD.md with the reason.
Out of scope
The Part B window and any step of it, the slot file, the uid separation (J2), and P19 407 and 408.