Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
7.4 KiB
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, thengit apply --index build.patch.sha256sum -con the manifest: 3/3 OK. The manifest file hashes toc6aaac1f...54af. The cached diff is byte-identical to build.patch, patch-id17a90c4ad795. Diffstat: 3 files, +430/-32. - Base drift:
4b738e2da..d63cfca22touches none of the three manifest paths. - Path readers and pins: only
packages/cli/tests/stack-release.test.mjsreadsscripts/stack-release.sh. No test orscripts/test-*.shreadsdocs/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, andsha256sum -cinsiderehearsal/passes. - Tests, one file at a time, scratch TMPDIR:
stack-release.test.mjs93/93 pass, 0 fail, 0 skip (23:11:46Z-23:14:47Z);stack-copy.test.mjs12/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
--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-targetandpm-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 isresult ok pm pending.
- parse_opts refuses it together with
- The pending state:
- read_pointer (:95) accepts pmPending only when it is missing, or true beside pm false.
- do_activate refuses
pm-pendingright 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.
- pm-on (:890-917):
- It refuses
not-pending, thenharness-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.
- It refuses
- Rollback and kinds:
- From pending, run_pm sets RUN_PM=1, so OLD is installed with
--pmand gated with pm_launched. - An in-run failure gates
P_COPYwithP_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.setwith null deletes the key (plugin-settings.mjs,delete next.plugins[kind]), so the brief's "passes after harness is set to null" holds.
- From pending, run_pm sets RUN_PM=1, so OLD is installed with
--plan: check_refuse (:257-262) collects the refusals. plan prints the pending lines and therefuseslines, 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-downbranch it follows stops the Console (stack-release.sh:881). Suggested wording: "Neither touches the copy or the store, and onlypm-on-downstops the Console." - F2 docs/TOOLS.md:426, :446: "
plugins setthrough the live bus" names no command that works on a copy atd195d171c, becausescripts/mosaicthere doesn't dispatchplugins. 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-unreadablerefusal (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 withoutpackages/plugins/src/kinds.mjs(anything beforeeaeb5ebf2, 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 --planprints a bus line with--pmabove itsrefuses pm-pendingline. That's harmless, since the plan exits 3.