plans: D1 brief item 8 re-pinned to the held or stopped store read; stack-release hardening brief (row 162 follow-up)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
jason.woltjeandClaude Opus 5.5 committed 2026-10-18 15:39:38 -05:00
1 parent f5b5aa509f
commit c70f360ae7
2 files changed
+72 -5

No files matched your search

@@ -0,0 +1,64 @@
# 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
1. Precheck unreadable drop-ins. `drop_in_override` (:503) scans the same three directories
as `install_guard` in `scripts/bus-service.sh` (:100), but skips a directory or file it
can't read (`[ -d ]`, `grep 2>/dev/null`). `install_guard` dies on that same path after the
stop, so the run rolls back and the live stack stopped for nothing. The precheck refuses
`drop-in-override` with the path when a unit path, drop-in directory or `.conf` file exists
but can't be read, using `install_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 any
`systemctl stop` in the stand-in log, and the units stay active.
2. Close the `is-active` to read race. `plan_store` (:573, check at :577) and the `--restore`
event count (:716) choose the held-store read or the stopped-store read from one
`is-active` call. If the bus stops between the check and the readOnly open, the open can
leave `-wal` or `-shm` beside a stopped store, and the next run refuses `store-unclean`.
After a held-store read, check `is-active` again. If the bus stopped meanwhile, say so:
`--plan` prints that the store state changed during the read and prints no migration
line; rollback refuses `restore-mismatch` before anything stops.
Test: a stand-in `systemctl` that answers active, then inactive.
3. Compare the copy check with the target. The `copy-check` gate step (:373) runs
`stack-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 with `copy-check` on a mismatch.
Test: a stand-in check that reports agreement on another copy fails the gate and rolls
back.
4. Don't split a quoted `Environment=`. `read_units` (:148) splits the `systemctl show`
`Environment=` line (`show` at :144) on spaces with `read -r -a` (:157). systemd quotes a
value that holds a space, so a `MOSAIC_CLAUDE_BIN` path with a space reads as a prefix of
itself. Smallest fix: `read_units` returns 1 when that line holds a quote or backslash, so
its callers refuse as they do for an unreadable unit (`unit-flags-drift` at :535,
`check-failed` at :561; the status view at :743 then skips its unit flags line). Cover the
`ExecStart` argv split (`argv_of`, :145, used at :153 and :159) the same way.
Test: a stand-in unit with `Environment="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.mjs` and every `scripts/test-*.sh` green 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.
+8 -5
View File
@@ -178,11 +178,14 @@ start has no gate that puts the old copy back.
refuses `post-backup-events` unless `--events N` repeats the printed
count.
8. `activate ID BUSINESS --plan` and `rollback BUSINESS --plan` print
the target, whether a migration or a restore is expected (from the
pointer's `schema` and the target copy's `SCHEMA_VERSION`, with no
store read) and the flags that would be installed. They change
nothing and write no log line, so the window knows beforehand
whether Jason must be present.
the target, whether a migration or a restore is expected and the
flags that would be installed. The store schema comes from a
read-only read of the store the running bus holds, or of the stopped
store when the bus is down. With no store file it falls back to the
pointer's `schema` and says so (sage-0745, mos-0741; this replaced
the first draft's "pointer only, no store read"). The target side is the
copy's `SCHEMA_VERSION`. They change nothing and write no log line,
so the window knows beforehand whether Jason must be present.
9. `status BUSINESS` prints the pointer, the last five log lines, the
live units' copies, the result of `stack-copy.sh check`, `bus status`
and the drift from `refactor`. It only reads.