Skip to content

fix(install-channel,hooks,docs): three gaps a post-merge review found - #208

Merged
kevintseng merged 2 commits into
mainfrom
fix/plugin-host-awareness-gaps
Aug 24, 2026
Merged

fix(install-channel,hooks,docs): three gaps a post-merge review found#208
kevintseng merged 2 commits into
mainfrom
fix/plugin-host-awareness-gaps

Conversation

@kevintseng

Copy link
Copy Markdown
Contributor

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

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 session-start.js:541 gives it 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; when they do, the name is absent from the path, detection returns null, and the install lands on unknown — 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 #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 as homeDir() in paths.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 left check-doc-claims at exit 0 and the whole suite green.

The predicate moved to scripts/lib/test-count-claim.mjs — the shape hook-files.mjs already uses — so a test can reach it.

Break-tests

Each mutant applied, run, then restored byte-identical.

Mutation Result
env lookup disabled exit=1 — 2 failed | 18 passed (follows a relocated home via CODEX_HOME / CLAUDE_CONFIG_DIR)
CJK branch removed exit=1the exact mutant that survived before this PR now fails 2 assertions
deprecation branch removed exit=1 — 3 failed | 3 passed (reintroduces bug 1 verbatim)

Evidence

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
npm run verify:release                exit=0   all detectors new=0

Deliberately not fixed

  • The Codex command hardcodes the pcircle-memesh marketplace name though the path carries it (<home>/.codex/plugins/cache/<MARKETPLACE>/…). Only matters for a private mirror; none exists.
  • PluginHost has 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.

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
@kevintseng
kevintseng merged commit 7608578 into main Aug 24, 2026
13 checks passed
@kevintseng
kevintseng deleted the fix/plugin-host-awareness-gaps branch August 24, 2026 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant