fix(install-channel,hooks,docs): three gaps a post-merge review found - #208
Merged
Conversation
Found reviewing what this session had already merged (#203 #204 #206 #207) — two by a cross-model adversarial pass, one by mutation-testing my own work. None break what shipped; all three are guards that do not guard. 1. The security-advisory banner never learned about plugin installs. `buildDeprecationBanner` branched npm-global / source-checkout / npm-local and fell everything else through to "fetch the latest @pcircle/memesh from npm". It had no `plugin-marketplace` branch at all — and it takes precedence over `buildUpdateAvailableBanner`, which DOES have one. So the message that fires on a maintainer security advisory carried the least actionable instruction, on both hosts, while the routine out-of-date message carried the right one. The cause is that the decision was written down twice. Both banners now call one `pluginUpgradeLine()`; that is the part that stops it recurring, not the added branch. 2. A relocated CODEX_HOME / CLAUDE_CONFIG_DIR fell back to `unknown`. `detectPluginHost` matched the literal `.codex` / `.claude` directory name. Both runtimes let the user move that directory, and when they do the name is absent from the path — so detection returned null and the install landed on `unknown`, which is the exact answer #207 existed to stop giving. `src/core/setup.ts:29` had already written this hazard down — "CODEX_HOME can relocate the whole directory" — and used it to REJECT a substring check over config.toml. The matcher I shipped was that same rejected shape. It now checks the documented env var too, keeping the segment match because the running process's home is not necessarily the home the package lives under. An env var exporting "" counts as unset, same reasoning as homeDir(). 3. The docs gate's CJK branch had nothing watching it. #206 widened the "no README states a test count" regex to catch README.zh-TW.md's "630 項測試" — and deleted that line in the same change. So the branch had nothing left to match and no test named it. Proven, not suspected: deleting `|\d[\d,]*\s*項測試` outright left `check-doc-claims` at exit 0 and the whole suite green. The predicate moved to `scripts/lib/test-count-claim.mjs` — the same shape `hook-files.mjs` already uses — so a test can reach it. Break-tested, all three, restored byte-identical after each: env lookup disabled exit=1, 2 failed | 18 passed follows a relocated home via CODEX_HOME / CLAUDE_CONFIG_DIR CJK branch removed exit=1, the exact mutant that SURVIVED before this commit now fails 2 assertions deprecation branch removed exit=1, 3 failed | 3 passed (this reintroduces bug 1 verbatim) node scripts/run-tests-isolated.mjs exit=0 Test Files 188 passed (188) Tests 2592 passed (2592) npm run typecheck exit=0 npm run build exit=0 Not fixed, deliberately: the Codex upgrade command hardcodes the `pcircle-memesh` marketplace name though the path carries it, and PluginHost has no third-host branch. Both are real; neither has a caller today, and inventing one would be building for a user that does not exist.
PR #208 shipped without a CHANGELOG entry — noticed only when asked why I had stopped, which is the honest reason it is a separate commit. Two of the three fixes are here, not three. Checked each against the v4.7.1 tag rather than assuming: deprecation banner buildDeprecationBanner in v4.7.1 has zero plugin-marketplace branches -> a released user has this bug -> entry belongs relocated home v4.7.1's install-channel.ts matches a literal `.claude` with no env handling -> a released user with CLAUDE_CONFIG_DIR set has this bug -> entry belongs, led by the Claude Code case because CODEX_HOME only exists in unreleased code CJK docs gate v4.7.1's check-doc-claims.mjs contains no 項測試 at all -> the widening AND its gap both live entirely inside this unreleased cycle -> NOT an entry The third was written and then removed. It described an internal release gate fixing a bug that only ever existed between two unreleased commits: to a reader on 4.7.1 it is a fix to something they never had, which reads as a product defect they should worry about. Same mistake the v4.6.0 release notes made. node scripts/check-doc-claims.mjs exit=0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found by reviewing what this session had already merged (#203 #204 #206 #207): two by a cross-model adversarial pass, one by mutation-testing my own work. None break what shipped. All three are guards that do not guard.
1. The security-advisory banner never learned about plugin installs
buildDeprecationBannerbranchednpm-global/source-checkout/npm-localand fell everything else through to "fetch the latest @pcircle/memesh from npm". It had noplugin-marketplacebranch at all — andsession-start.js:541gives it precedence overbuildUpdateAvailableBanner, which does have one.So the message that fires on a maintainer security advisory carried the least actionable instruction, on both hosts, while the routine out-of-date message carried the right one.
The cause is that the decision was written down twice. Both banners now call one
pluginUpgradeLine()— that is the part that stops it recurring, not the added branch.2. A relocated
CODEX_HOME/CLAUDE_CONFIG_DIRfell back tounknowndetectPluginHostmatched the literal.codex/.claudedirectory name. Both runtimes let the user move that directory; when they do, the name is absent from the path, detection returns null, and the install lands onunknown— the exact answer #207 existed to stop giving.src/core/setup.ts:29had already written this hazard down — "CODEX_HOME can relocate the whole directory" — and used it to reject a substring check overconfig.toml. The matcher #207 shipped was that same rejected shape.Now checks the documented env var too, keeping the segment match because the running process's home is not necessarily the home the package lives under (shared / multi-user install). An env var exporting
""counts as unset — same reasoning ashomeDir()inpaths.ts.3. The docs gate's CJK branch had nothing watching it
#206 widened the "no README states a test count" regex to catch README.zh-TW.md's
630 項測試— and deleted that line in the same change. The branch had nothing left to match and no test named it.Proven, not suspected: deleting
|\d[\d,]*\s*項測試outright leftcheck-doc-claimsat exit 0 and the whole suite green.The predicate moved to
scripts/lib/test-count-claim.mjs— the shapehook-files.mjsalready uses — so a test can reach it.Break-tests
Each mutant applied, run, then restored byte-identical.
exit=1— 2 failed | 18 passed (follows a relocated home via CODEX_HOME/CLAUDE_CONFIG_DIR)exit=1— the exact mutant that survived before this PR now fails 2 assertionsexit=1— 3 failed | 3 passed (reintroduces bug 1 verbatim)Evidence
Deliberately not fixed
pcircle-memeshmarketplace name though the path carries it (<home>/.codex/plugins/cache/<MARKETPLACE>/…). Only matters for a private mirror; none exists.PluginHosthas no third-host branch — all consumers do=== 'codex' ? … : 'Claude Code'.Both are real. Neither has a caller today, and inventing one would be building for a user that does not exist.