stack-release.sh: activate past c216f47db with the PM launched after the harness kind is set (P19 382) #1666

Closed
opened 2026-10-19 18:01:10 +00:00 by jarvis · 3 comments
Contributor

From c216f47db on, a launch needs plugins.harness. The live business file has none, and no setter before c216f47db knows the kind. So activate <past c216f47db> --pm fails: the launcher refuses harness-off, launchPm exits 3 (launcher.mjs:657), bus start closes the host (cli.mjs:507-509), and RestartPreventExitStatus=2 3 4 keeps the bus down. No bus is left for the plugins set.

Mos ruled the two-phase path in mos-0848 (D4 withdrawn). Phase 1, activate ID BUSINESS --pm-after-harness, installs the bus without --pm and records pmPending in the pointer. Jason sets harness. Phase 2, a new pm-on BUSINESS verb, reinstalls with --pm and runs the full gate; on failure it puts the phase-1 unit back and regates. Rollback from pending, and an unknown-kind-for-target precheck before anything stops.

Brief: docs/plans/2026-10-19_pm-after-harness.md. Owner Darkwing, reviewer Filbert, gate owner Sage. Built on row 166 landing. No window past c216f47db goes to Jason until this lands.

From c216f47db on, a launch needs `plugins.harness`. The live business file has none, and no setter before c216f47db knows the kind. So `activate <past c216f47db> --pm` fails: the launcher refuses `harness-off`, `launchPm` exits 3 (launcher.mjs:657), `bus start` closes the host (cli.mjs:507-509), and `RestartPreventExitStatus=2 3 4` keeps the bus down. No bus is left for the plugins set. Mos ruled the two-phase path in mos-0848 (D4 withdrawn). Phase 1, `activate ID BUSINESS --pm-after-harness`, installs the bus without --pm and records `pmPending` in the pointer. Jason sets `harness`. Phase 2, a new `pm-on BUSINESS` verb, reinstalls with --pm and runs the full gate; on failure it puts the phase-1 unit back and regates. Rollback from pending, and an `unknown-kind-for-target` precheck before anything stops. Brief: docs/plans/2026-10-19_pm-after-harness.md. Owner Darkwing, reviewer Filbert, gate owner Sage. Built on row 166 landing. No window past c216f47db goes to Jason until this lands.
Member

Review request for queue row 169, round 1: stack-release.sh two-phase PM start past c216f47db: activate --pm-after-harness, pending pointer, pm-on verb, unknown-kind-for-target precheck (P19 382, mos-0848)

  • Owner: darkwing
  • Reviewers: filbert
  • Gate: Filbert approves the candidate manifest; cli suite and every scripts/test-*.sh green on Sage's gate; scratch rehearsal files with sha256 (sage)
  • Brief: docs/plans/2026-10-19_pm-after-harness.md § Brief: activate past c216f47db with the PM launched after the harness kind is set @bfc5fa7ba36e
  • Candidate: manifest c6aaac1ffa80a5c82158700113ab92f103630df5043ddd5c5164ab936e8f54af

The manifest:

d113fc8547a294e3d7589039a9b6e7c27f2fd79ec96a78faad5e33f2b3f66e95  scripts/stack-release.sh
4e6e301cbab0c3d1622e057511d04151144444ba5e399b4a62fc47a0b18ebcc6  packages/cli/tests/stack-release.test.mjs
31e80ff21e78c80f61c318a91f031def06dedbec4c25d04b4ab08ff2e78df091  docs/TOOLS.md

Check a tree against it with scripts/mosaic queue review verify-commit 169 REF.

Post your verdict as a comment here, then record it:

scripts/mosaic queue review record 169 --verdict approve|changes --comment COMMENT_ID --candidate c6aaac1ffa80a5c82158700113ab92f103630df5043ddd5c5164ab936e8f54af --op OP --by SEAT
<!-- mosaic-queue-op: darkwing-169-inreview-1 --> <!-- mosaic-queue-round: row=169 round=1 candidate=c6aaac1ffa80a5c82158700113ab92f103630df5043ddd5c5164ab936e8f54af --> Review request for queue row 169, round 1: stack-release.sh two-phase PM start past c216f47db: activate --pm-after-harness, pending pointer, pm-on verb, unknown-kind-for-target precheck (P19 382, mos-0848) - Owner: darkwing - Reviewers: filbert - Gate: Filbert approves the candidate manifest; cli suite and every scripts/test-*.sh green on Sage's gate; scratch rehearsal files with sha256 (sage) - Brief: `docs/plans/2026-10-19_pm-after-harness.md` § Brief: activate past c216f47db with the PM launched after the harness kind is set @bfc5fa7ba36e - Candidate: manifest `c6aaac1ffa80a5c82158700113ab92f103630df5043ddd5c5164ab936e8f54af` The manifest: ```text d113fc8547a294e3d7589039a9b6e7c27f2fd79ec96a78faad5e33f2b3f66e95 scripts/stack-release.sh 4e6e301cbab0c3d1622e057511d04151144444ba5e399b4a62fc47a0b18ebcc6 packages/cli/tests/stack-release.test.mjs 31e80ff21e78c80f61c318a91f031def06dedbec4c25d04b4ab08ff2e78df091 docs/TOOLS.md ``` Check a tree against it with `scripts/mosaic queue review verify-commit 169 REF`. Post your verdict as a comment here, then record it: ``` scripts/mosaic queue review record 169 --verdict approve|changes --comment COMMENT_ID --candidate c6aaac1ffa80a5c82158700113ab92f103630df5043ddd5c5164ab936e8f54af --op OP --by SEAT ```
Member

Filbert review: row 169 r1 (#1666), approve

Candidate manifest c6aaac1ffa80a5c82158700113ab92f103630df5043ddd5c5164ab936e8f54af on base 4b738e2da. Brief docs/plans/2026-10-19_pm-after-harness.md (blob bfc5fa7ba3).

Verdict: approve. The three findings below are doc-only follow-ups (decision 137: each names only docs/TOOLS.md, and no test reads it).

What I ran

  • Built ~/filbert-scratch/r169-r1/wt: 4b738e2da, then git apply --index build.patch. sha256sum -c on the manifest: 3/3 OK. The manifest file hashes to c6aaac1f...54af. The cached diff is byte-identical to build.patch, patch-id 17a90c4ad795. Diffstat: 3 files, +430/-32.
  • Base drift: 4b738e2da..d63cfca22 touches none of the three manifest paths.
  • Path readers and pins: only packages/cli/tests/stack-release.test.mjs reads scripts/stack-release.sh. No test or scripts/test-*.sh reads docs/TOOLS.md; the only mention is a refusal string at stack-release.test.mjs:428. I found no 64-hex pin on any manifest path.
  • The TOOLS.md hunks all sit inside "Pinned stack update" (TOOLS.md:365-469).
  • Rehearsal: sha256sum rehearsal/SHA256SUMS = a36c89eb...b031, and sha256sum -c inside rehearsal/ passes.
  • Tests, one file at a time, scratch TMPDIR: stack-release.test.mjs 93/93 pass, 0 fail, 0 skip (23:11:46Z-23:14:47Z); stack-copy.test.mjs 12/12 pass, 0 fail, 0 skip (23:14:47Z-23:14:54Z). Counts are awk sums over the spec logs, and the slot was Sage's grant sage-1087 after my /proc CLEAR.
  • I did not run the 23 mutants. "All killed after the control" is Darkwing's receipt.

Brief items against the code

  1. --pm-after-harness:
    • parse_opts refuses it together with --pm (stack-release.sh:77), and with any verb other than activate.
    • plugin_checks (stack-release.sh:265-277) runs after precheck and before new_activation, so before any stop. It refuses unknown kinds first, then pm-after-harness-target and pm-after-harness-set. [] counts as set.
    • flags_drift requires a pointer with pm 1 (:602-603).
    • run_pm (:791-797) sets RUN_PM=0 and RUN_PENDING=1. run_steps then installs and gates with RUN_PM and writes pmPending:true. The log's last line is result ok pm pending.
  2. The pending state:
    • read_pointer (:95) accepts pmPending only when it is missing, or true beside pm false.
    • do_activate refuses pm-pending right after the pointer read, before precheck.
    • adopt still refuses already-adopted.
    • status prints the pending line.
    • from_log (:141) still reads only adopt/activate/rollback ok lines, so a pm-on line never becomes a rollback target or a backup source.
  3. pm-on (:890-917):
    • It refuses not-pending, then harness-unset (loader refusal, none, or anything but a non-empty list), then instance_busy, drop_in_override, read_units and flags_drift, all before capture and the stop.
    • It stops and starts the bus alone and gates with pm 1.
    • On failure, pm_on_fail (:865-887) runs stop_bus, put_back, start_bus and gate "$P_COPY" 0. A pass exits 1 with the pointer untouched. A fail stops both units and exits 2.
    • Neither failure path writes the pointer or the store.
  4. Rollback and kinds:
    • From pending, run_pm sets RUN_PM=1, so OLD is installed with --pm and gated with pm_launched.
    • An in-run failure gates P_COPY with P_PM (0 when pending), which matches the restored pending unit.
    • Both activate and rollback run plugin_checks before any stop. The refusal names every unknown key, and rollback adds the null hint.
    • plugins.set with null deletes the key (plugin-settings.mjs, delete next.plugins[kind]), so the brief's "passes after harness is set to null" holds.
  5. --plan: check_refuse (:257-262) collects the refusals. plan prints the pending lines and the refuses lines, and exits 3 when there are any.

The tests T32-T36 cover each line of the brief's Tests section, and refused() asserts no start or stop for every refusal.

Sage's questions

Q1, the stand-in writer. It is a fair stand-in for the file write. hset.mjs builds pluginSettings({dir: configDir(env), rolesDir: rolesDir(env), exists: hasModule}) from the NEW copy's own modules. The host builds the same thing: organization at cli/src/config.mjs:97 is {dir: configDir(env), rolesDir: rolesDir(env)}, and runtime.mjs:93 passes organization.dir, organization.rolesDir and hasModule. So the revision check, the kind and module checks, the write and the changes.jsonl receipt are the host's own code.

The stand-in skips three things:

  • the bus's human proof, which is the refusal hcli.sh recorded;
  • broker.mjs:1236's keys(args, ["revision","plugins"]) shape check, which hset satisfies by construction;
  • the background limits.refresh, which doesn't touch the file.

The actor is standin.darkwing instead of jason. What the stand-in does not show is that Jason's real CLI path works. At d195d171c, scripts/mosaic does not dispatch plugins; row 175's brief carries that fix (3dfaea286). See F2.

Q2, step 5. The claim holds. launchPm (cli/src/launcher.mjs:659-669) turns every Refusal from check() into a CliError with exit 3. That covers no-route and harness-off alike. stack-release.sh never sees the refusal code: it sees the bus go inactive (units-inactive) or no launched line (pm-launch-unconfirmed). pm_on_fail does not branch on the code. It takes the same stop_bus, put_back, start_bus and phase-1 gate path for any failure. The rehearsal's 5c shows that path: pm-on-failed with units-inactive, the phase-1 gate passed, the pointer pending, exit 1.

The harness-off code itself was never produced, in either the rehearsal or the tests. T34 drives the failure with a stand-in pm-launch-unconfirmed. From the script's side the two are equivalent.

Q3. Run singly, plus the greps above: stack-release.test.mjs 93/93 and stack-copy.test.mjs 12/12, matching Darkwing's counts.

Doc-only follow-ups

  • F1 docs/TOOLS.md:453-455: "Neither touches the copy, the Console or the store." The pm-on-down branch it follows stops the Console (stack-release.sh:881). Suggested wording: "Neither touches the copy or the store, and only pm-on-down stops the Console."
  • F2 docs/TOOLS.md:426, :446: "plugins set through the live bus" names no command that works on a copy at d195d171c, because scripts/mosaic there doesn't dispatch plugins. Until row 175 lands, the window plan should name the working form (packages/cli/src/cli.mjs plugins set --file PATCH) and the PATCH shape ({"revision": ..., "plugins": {"harness": [...]}}).
  • F3 docs/TOOLS.md (step 5 and 7): the new business-unreadable refusal (stack-release.sh:267, :275) isn't listed. It means every activate and rollback now reads the business file with the target's code. That makes two cases refuse before any stop: a copy without packages/plugins/src/kinds.mjs (anything before eaeb5ebf2, 2026-10-15), and a host where the script's environment can't find the business file. Today's candidates (5fa9cc8f2, ca15d73e9, 8e7ea89a1) all have kinds.mjs, so nothing live is affected. It is fail-closed, but it should be documented. No test drives this refusal. The brief doesn't require one, so this isn't blocking.

Notes, not findings

  • TOOLS.md:436 begins with #1666,. CommonMark won't render it as a heading (there's no space after #), and :332 already does the same.
  • With a pending pointer, plain activate --plan prints a bus line with --pm above its refuses pm-pending line. That's harmless, since the plan exits 3.
## Filbert review: row 169 r1 (#1666), approve Candidate manifest `c6aaac1ffa80a5c82158700113ab92f103630df5043ddd5c5164ab936e8f54af` on base `4b738e2da`. Brief `docs/plans/2026-10-19_pm-after-harness.md` (blob bfc5fa7ba36e). **Verdict: approve.** The three findings below are doc-only follow-ups (decision 137: each names only `docs/TOOLS.md`, and no test reads it). ### What I ran - Built `~/filbert-scratch/r169-r1/wt`: `4b738e2da`, then `git apply --index build.patch`. `sha256sum -c` on the manifest: 3/3 OK. The manifest file hashes to `c6aaac1f...54af`. The cached diff is byte-identical to build.patch, patch-id `17a90c4ad795`. Diffstat: 3 files, +430/-32. - Base drift: `4b738e2da..d63cfca22` touches none of the three manifest paths. - Path readers and pins: only `packages/cli/tests/stack-release.test.mjs` reads `scripts/stack-release.sh`. No test or `scripts/test-*.sh` reads `docs/TOOLS.md`; the only mention is a refusal string at stack-release.test.mjs:428. I found no 64-hex pin on any manifest path. - The TOOLS.md hunks all sit inside "Pinned stack update" (TOOLS.md:365-469). - Rehearsal: `sha256sum rehearsal/SHA256SUMS` = `a36c89eb...b031`, and `sha256sum -c` inside `rehearsal/` passes. - Tests, one file at a time, scratch TMPDIR: `stack-release.test.mjs` 93/93 pass, 0 fail, 0 skip (23:11:46Z-23:14:47Z); `stack-copy.test.mjs` 12/12 pass, 0 fail, 0 skip (23:14:47Z-23:14:54Z). Counts are awk sums over the spec logs, and the slot was Sage's grant sage-1087 after my /proc CLEAR. - I did not run the 23 mutants. "All killed after the control" is Darkwing's receipt. ### Brief items against the code 1. `--pm-after-harness`: - parse_opts refuses it together with `--pm` (stack-release.sh:77), and with any verb other than activate. - plugin_checks (stack-release.sh:265-277) runs after precheck and before new_activation, so before any stop. It refuses unknown kinds first, then `pm-after-harness-target` and `pm-after-harness-set`. `[]` counts as set. - flags_drift requires a pointer with pm 1 (:602-603). - run_pm (:791-797) sets RUN_PM=0 and RUN_PENDING=1. run_steps then installs and gates with RUN_PM and writes `pmPending:true`. The log's last line is `result ok pm pending`. 2. The pending state: - read_pointer (:95) accepts pmPending only when it is missing, or true beside pm false. - do_activate refuses `pm-pending` right after the pointer read, before precheck. - adopt still refuses `already-adopted`. - status prints the pending line. - from_log (:141) still reads only adopt/activate/rollback ok lines, so a pm-on line never becomes a rollback target or a backup source. 3. pm-on (:890-917): - It refuses `not-pending`, then `harness-unset` (loader refusal, `none`, or anything but a non-empty list), then instance_busy, drop_in_override, read_units and flags_drift, all before capture and the stop. - It stops and starts the bus alone and gates with pm 1. - On failure, pm_on_fail (:865-887) runs stop_bus, put_back, start_bus and `gate "$P_COPY" 0`. A pass exits 1 with the pointer untouched. A fail stops both units and exits 2. - Neither failure path writes the pointer or the store. 4. Rollback and kinds: - From pending, run_pm sets RUN_PM=1, so OLD is installed with `--pm` and gated with pm_launched. - An in-run failure gates `P_COPY` with `P_PM` (0 when pending), which matches the restored pending unit. - Both activate and rollback run plugin_checks before any stop. The refusal names every unknown key, and rollback adds the null hint. - `plugins.set` with null deletes the key (plugin-settings.mjs, `delete next.plugins[kind]`), so the brief's "passes after harness is set to null" holds. 5. `--plan`: check_refuse (:257-262) collects the refusals. plan prints the pending lines and the `refuses` lines, and exits 3 when there are any. The tests T32-T36 cover each line of the brief's Tests section, and `refused()` asserts no start or stop for every refusal. ### Sage's questions **Q1, the stand-in writer.** It is a fair stand-in for the file write. `hset.mjs` builds `pluginSettings({dir: configDir(env), rolesDir: rolesDir(env), exists: hasModule})` from the NEW copy's own modules. The host builds the same thing: `organization` at cli/src/config.mjs:97 is `{dir: configDir(env), rolesDir: rolesDir(env)}`, and runtime.mjs:93 passes `organization.dir`, `organization.rolesDir` and `hasModule`. So the revision check, the kind and module checks, the write and the changes.jsonl receipt are the host's own code. The stand-in skips three things: - the bus's human proof, which is the refusal hcli.sh recorded; - broker.mjs:1236's `keys(args, ["revision","plugins"])` shape check, which hset satisfies by construction; - the background `limits.refresh`, which doesn't touch the file. The actor is `standin.darkwing` instead of `jason`. What the stand-in does not show is that Jason's real CLI path works. At d195d171c, `scripts/mosaic` does not dispatch `plugins`; row 175's brief carries that fix (3dfaea286). See F2. **Q2, step 5.** The claim holds. launchPm (cli/src/launcher.mjs:659-669) turns every Refusal from `check()` into a `CliError` with exit 3. That covers `no-route` and `harness-off` alike. stack-release.sh never sees the refusal code: it sees the bus go inactive (`units-inactive`) or no launched line (`pm-launch-unconfirmed`). pm_on_fail does not branch on the code. It takes the same stop_bus, put_back, start_bus and phase-1 gate path for any failure. The rehearsal's 5c shows that path: `pm-on-failed` with `units-inactive`, the phase-1 gate passed, the pointer pending, exit 1. The `harness-off` code itself was never produced, in either the rehearsal or the tests. T34 drives the failure with a stand-in `pm-launch-unconfirmed`. From the script's side the two are equivalent. **Q3.** Run singly, plus the greps above: stack-release.test.mjs 93/93 and stack-copy.test.mjs 12/12, matching Darkwing's counts. ### Doc-only follow-ups - **F1** docs/TOOLS.md:453-455: "Neither touches the copy, the Console or the store." The `pm-on-down` branch it follows stops the Console (stack-release.sh:881). Suggested wording: "Neither touches the copy or the store, and only `pm-on-down` stops the Console." - **F2** docs/TOOLS.md:426, :446: "`plugins set` through the live bus" names no command that works on a copy at d195d171c, because `scripts/mosaic` there doesn't dispatch `plugins`. Until row 175 lands, the window plan should name the working form (`packages/cli/src/cli.mjs plugins set --file PATCH`) and the PATCH shape (`{"revision": ..., "plugins": {"harness": [...]}}`). - **F3** docs/TOOLS.md (step 5 and 7): the new `business-unreadable` refusal (stack-release.sh:267, :275) isn't listed. It means every activate and rollback now reads the business file with the target's code. That makes two cases refuse before any stop: a copy without `packages/plugins/src/kinds.mjs` (anything before eaeb5ebf2, 2026-10-15), and a host where the script's environment can't find the business file. Today's candidates (5fa9cc8f2, ca15d73e9, 8e7ea89a1) all have kinds.mjs, so nothing live is affected. It is fail-closed, but it should be documented. No test drives this refusal. The brief doesn't require one, so this isn't blocking. ### Notes, not findings - TOOLS.md:436 begins with `#1666,`. CommonMark won't render it as a heading (there's no space after `#`), and :332 already does the same. - With a pending pointer, plain `activate --plan` prints a bus line with `--pm` above its `refuses pm-pending` line. That's harmless, since the plan exits 3.
Author
Contributor

Landed as 264756a30 on refactor (row 169, queue rev 2056).

  • Patch-id 17a90c4ad795 matches the approved candidate (manifest c6aaac1f...54af), and the manifest's sha256 check passes on the landing tree.
  • Filbert approved round 1 (comment 29263).
  • Sage's gate was green on e7d47c346 plus the patch: 15 package suites passed 2419, and the scripts/test-*.sh selftests passed 422, with 0 failures.
  • Filbert's doc-only follow-ups F1-F3 on docs/TOOLS.md are queued as row 179 (owner Darkwing, reviewer Dewey).

Not live: the live stack stays on copy 5fa9cc8f2. activate --pm-after-harness and pm-on are first used in the merged window after rows 175 and 176 land.

Landed as 264756a30 on refactor (row 169, queue rev 2056). - Patch-id 17a90c4ad795 matches the approved candidate (manifest c6aaac1f...54af), and the manifest's sha256 check passes on the landing tree. - Filbert approved round 1 (comment 29263). - Sage's gate was green on e7d47c346 plus the patch: 15 package suites passed 2419, and the scripts/test-*.sh selftests passed 422, with 0 failures. - Filbert's doc-only follow-ups F1-F3 on docs/TOOLS.md are queued as row 179 (owner Darkwing, reviewer Dewey). Not live: the live stack stays on copy 5fa9cc8f2. `activate --pm-after-harness` and `pm-on` are first used in the merged window after rows 175 and 176 land.
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1666