4.0 KiB
Brief: stack-release.sh hardening after D1
Follow-up to row 162 (D1, #1659), from Filbert's non-blocking notes on round 1 (#1659 comment 28997). Darkwing drafted it (agents/darkwing/work/queue-162/followup-brief.md). Sage folded it in here and made the fifth note, the brief item 8 re-pin, a doc change in docs/plans/2026-10-18_stack-release.md.
Owner Darkwing, reviewer Filbert, gate owner Sage. Starts after row 162 lands and builds on
that landing. Line numbers below are scripts/stack-release.sh as landed by row 162.
None of the four is a known failure today. Each one fails closed or can't be reached with the
current pin. The fix makes each one refuse earlier, or tell the truth sooner, with codes row
162 already has. No new refusal code. Nothing here runs against the live stack: every test
uses the stand-in systemctl, units and stores the row 162 tests already use.
Items
-
Precheck unreadable drop-ins.
drop_in_override(:503) scans the same three directories asinstall_guardinscripts/bus-service.sh(:100), but skips a directory or file it can't read ([ -d ],grep 2>/dev/null).install_guarddies on that same path after the stop, so the run rolls back and the live stack stopped for nothing. The precheck refusesdrop-in-overridewith the path when a unit path, drop-in directory or.conffile exists but can't be read, usinginstall_guard's own tests (-r,-x, a failed read). Test: a drop-in file with mode 000, and a directory with mode 000, each refuse before anysystemctl stopin the stand-in log, and the units stay active. -
Close the
is-activeto read race.plan_store(:573, check at :577) and the--restoreevent count (:716) choose the held-store read or the stopped-store read from oneis-activecall. If the bus stops between the check and the readOnly open, the open can leave-walor-shmbeside a stopped store, and the next run refusesstore-unclean. After a held-store read, checkis-activeagain. If the bus stopped meanwhile, say so:--planprints that the store state changed during the read and prints no migration line; rollback refusesrestore-mismatchbefore anything stops. Test: a stand-insystemctlthat answers active, then inactive. -
Compare the copy check with the target. The
copy-checkgate step (:373) runsstack-copy.sh check --business, which proves the bus, Console and CLI agree on one copy, not that it is the copy this run installed. Pass the expected copy id, or compare the check's reported id with the target, and fail the gate withcopy-checkon a mismatch. Test: a stand-in check that reports agreement on another copy fails the gate and rolls back. -
Don't split a quoted
Environment=.read_units(:148) splits thesystemctl showEnvironment=line (showat :144) on spaces withread -r -a(:157). systemd quotes a value that holds a space, so aMOSAIC_CLAUDE_BINpath with a space reads as a prefix of itself. Smallest fix:read_unitsreturns 1 when that line holds a quote or backslash, so its callers refuse as they do for an unreadable unit (unit-flags-driftat :535,check-failedat :561; the status view at :743 then skips its unit flags line). Cover theExecStartargv split (argv_of, :145, used at :153 and :159) the same way. Test: a stand-in unit withEnvironment="MOSAIC_CLAUDE_BIN=/a b/claude"refuses by name and never reads/a.
Acceptance
- Each item's test, plus a mutant per item that removes the new check and fails that test.
packages/cli/tests/stack-release.test.mjsand everyscripts/test-*.shgreen on Sage's gate; Filbert approves the candidate manifest.- The diff touches only
scripts/stack-release.sh, its test file and, if a verb's wording changes, the TOOLS.md "Pinned stack update" section.
Out of scope
The bus-side PM marker, parsing systemd quoting, anything in Mos's live window, and any run
of adopt, activate or rollback against the live stack.