plans: brief for row 187, activate carries the row 178 proof pair (mos-0928 Part B A0, #1685)

This commit is contained in:
2026-10-20 03:42:11 -05:00
parent 1d7fd75e23
commit 6de3c86beb
@@ -0,0 +1,99 @@
# Brief: activate carries the row 178 proof pair
Sage, 2026-10-20, step A0 of mos-0928 Part B (`agents/sage/work/window-plan/repin-rows-177-185.md`
§ Part B). Gitea #1685. Owner Darkwing, reviewer Filbert, gate owner Sage. Starts after row 186, which touches the same
script. Base: the refactor head once row 186 lands. Line numbers are at 1d7fd75e2.
This row builds the tool only. Turning the proof on for the live Console is Part B, which waits for Jason
(J1-J6 on P19 385) and is not part of this row. Nothing here runs against `~/.mosaic-dev`, the live units
or the slot file. Every test uses the stand-in `systemctl`, units, stores and data root that
`packages/cli/tests/stack-release.test.mjs` already builds, with a proof file the test writes itself.
## Why
Row 178 gave the Console `--proof-header NAME --proof-file PATH` (`packages/webui/src/serve.mjs`
`proofOptions`, :217). On the pinned stack the Console flags live in the pointer. `flags_drift` (:627)
refuses `unit-flags-drift` when the unit differs, and `console_for_target` (:645) carries the pointer's
flags unchanged. So a hand `webui-service.sh install` with the pair would make every later `activate`,
`pm-on` and `rollback` refuse. `activate --tester-port N` is the only path that writes a new Console flag
into the pointer today. Part B's B3 needs the same path for the pair.
Part B's rollback also needs a way to turn the proof off. A plain `rollback` goes to the log's previous
distinct copy (`from_log previous`, :907). Today that is 89af38a83, which predates row 178, so the
rollback would drop the proof only by also dropping rows 177-185. An activate of the live copy without
the pair is the narrow way back, and today an activate carries the pair unchanged. So this row adds
`--no-proof` too.
## The change
1. `activate ID BUSINESS --proof-header NAME --proof-file PATH` writes the pair into `RUN_CONSOLE`, so
`install_stack` puts it in the Console unit and `write_pointer` puts it in the pointer, the same way
`--tester-port` does (:659-668). It replaces any pair the pointer already holds. Both flags or
neither; one alone is a usage error. Only `activate` takes them.
2. `activate ... --no-proof` drops any pair from `RUN_CONSOLE` and says so in the steps log, the way
`DROP_TESTER` does. With no pair in the pointer it is a no-op that says so. It can't go with the pair
flags (usage).
3. Before anything stops, the run refuses (exit 3, through `check_refuse` so `--plan` lists them) when:
- `proof-target`: the target copy's `packages/webui/src/cli.mjs` doesn't take `--proof-header`.
- `proof-needs-preview`: the Console flags the run would install lack any of `--public-host`,
`--auth-header` or `--allow-user`, which `proofOptions` needs.
- `proof-header-invalid`: the lowercased name doesn't match `^[a-z0-9][a-z0-9-]{0,63}$`, or is `host`,
`origin`, `x-mosaic-tester` or the lowercased `--auth-header` value. These are `proofOptions`'
own rules (serve.mjs :203, :220-222).
- `proof-file-invalid`: the path isn't absolute, holds a character outside `[A-Za-z0-9/._+-]` (the
unit argv can't quote, :166), is a symlink, isn't a regular file, isn't mode 600 or 400, or isn't
owned by the uid the script runs as (the Console unit's uid today; J2 changes that and gets its own
row).
4. The script never reads the proof file's contents. It checks the file with `stat` only. The value
never appears in the pointer, the log, the steps log, the plan output or any refusal text.
5. A `rollback` to a copy whose Console doesn't take `--proof-header` drops the pair and says so, as
`DROP_TESTER` does for `--tester-port`, since a rollback is the way back and must not refuse on it.
An activate to such a copy with a pair in the pointer refuses `proof-target` unless `--no-proof` is
given, so an activate never turns the proof off unless the operator asked. Unlike `--tester-port`,
the proof decides who can reach the Console. The plan prints the drop line. A copy that takes the
pair carries it unchanged, as today.
6. `--plan` prints one `proof:` line for an activate or rollback: `proof: on, header NAME, file PATH` for
a new pair, `proof: carried, header NAME` when the pointer's pair goes on unchanged, `proof: dropped;
...` with the reason, or `proof: off`.
7. `pm-on` is unchanged. It already writes the pointer's Console flags back as they are (:1008).
8. `docs/TOOLS.md` "Pinned stack update" gets the two flags in the usage block and one paragraph on the
pair, `--no-proof` and the drop.
## Tests
1. Activate with the pair: the pointer's `console` and the Console unit's argv end with
`--proof-header NAME --proof-file PATH`, after the preview flags and with `--tester-port` kept.
2. Activate of the live copy with the pair (Part B's B3 shape) passes and writes the pair.
3. A later activate without pair flags carries the pair; `pm-on` keeps it.
4. `--no-proof` drops it from the pointer and the unit, and the steps log says so. With no pair it says so
and passes.
5. Each refusal in change 3 exits 3 with no unit start or stop, and `--plan` lists it with any other
refusals.
6. Usage errors: one of the pair alone, `--no-proof` with the pair, either flag on `rollback` or `pm-on`.
7. Rollback to a copy without proof support drops the pair and says so. Rollback to one with it carries
the pair. Activate to a copy without support, with a pair in the pointer, refuses `proof-target`
without `--no-proof` and passes with it.
8. The value never leaks: the test's proof file holds a sentinel line, and no file under the test's state
or data root, and no stdout or stderr of any run, holds it.
9. `--plan` prints the `proof:` line for each case and writes nothing.
Mutants, each killed by some gate test (mos-0862):
- the run reads the proof file (any `cat` or `node` read of PATH)
- a target without proof support keeps the pair on rollback
- an activate to a target without proof support drops the pair without `--no-proof`
- `--no-proof` leaves the pair in the pointer but not the unit (drift)
- the mode check accepts 644
## Gate
Filbert approves the candidate manifest. Cli suite and every `scripts/test-*.sh` green on Sage's gate, run
from a git worktree of the candidate. Touched files: `scripts/stack-release.sh`,
`packages/cli/tests/stack-release.test.mjs`, `docs/TOOLS.md`, and the packet. Anything else gets named in
BUILD.md with the reason. No change to `packages/webui`.
## Out of scope
Running Part B or any step of it, writing the slot file (A1), the uid separation (J2), the k3s middleware
(J3, J4), and any change to the Console's proof rules. Going live needs a re-pin window that Mos checks
(A2), after Jason's go on Part B.