fix(plugins): use mergeHooksSettings() for marketplace hook supplement#1056
fix(plugins): use mergeHooksSettings() for marketplace hook supplement#1056rodrigoeqnit wants to merge 6 commits into
Conversation
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the fix. I validated the claim against the current loader path and agree with the root cause: in the hasManifest && strict marketplace supplement path, the old object spread replaced same-event hook arrays, while mergeHooksSettings() preserves the plugin manifest entries and appends the marketplace entries.
Please fix one thing before merge:
Missing Regression Coverage
src/utils/plugins/pluginLoader.ts:2919
The implementation change is correct, but this bug is exactly the kind of loader behavior that can regress silently. Please add a focused test that loads a marketplace plugin with:
plugin.jsondeclaring a hook for an event such asPreToolUse- the marketplace entry supplementing
hooksfor that same event - an assertion that the final
hooksConfig.PreToolUsecontains both matchers in order
I checked the current focused test file, src/utils/plugins/pluginLoader.test.ts, and it only covers mergePluginSources; it does not exercise finishLoadingPluginFromPath, marketplace supplementation, or hook merging.
Validation Performed
- Reviewed PR delta: one-file change in
src/utils/plugins/pluginLoader.ts - Checked linked issue #1055 and confirmed the PR addresses the described overwrite path
- Ran
bun test src/utils/plugins/pluginLoader.test.ts: passed, 2 tests - Ran
bun run typecheck: currently fails on broad unrelated baseline errors across the repo, so it did not provide PR-specific signal
gnanam1990
left a comment
There was a problem hiding this comment.
Thanks — root cause and fix are correct: switching the marketplace supplement path from object spread to mergeHooksSettings() matches what createPluginFromPath already does, so per-event matcher arrays from plugin.json and the marketplace entry are concatenated instead of overwritten.
Blocking:
- Missing regression coverage in
src/utils/plugins/pluginLoader.test.ts. The current suite only exercisesmergePluginSources, so this fix is unprotected against regression infinishLoadingPluginFromPath. Please add a focused test that loads a marketplace plugin whereplugin.jsondeclares a hook for, e.g.,PreToolUseand the marketplace entry supplementshooks.PreToolUse, asserting the merged config preserves both matchers in order.
Non-blocking:
- The inline comment is good and explains the invariant clearly.
- No CI runs are attached — once the test is added, CI will exercise the path.
Verified locally: read the diff, confirmed mergeHooksSettings() is already exported in the same file and used in createPluginFromPath. Did not run focused tests as no new test file exists yet.
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Targeted review of the current head (9826124).
Verdict: Needs changes
Blocking issue:
- The code change itself is the right fix, but this loader path still needs regression coverage. Please add a focused test where
plugin.jsondefines a hook for the same event as the marketplace entry supplement, then assert the finalhooksConfigcontains both matcher arrays in order.
This is exactly the kind of plugin loader behavior that can silently regress, so I do not want to merge it untested even though the implementation direction is correct.
Object spread silently overwrites plugin.json matchers for the same event when a marketplace entry also declares hooks for that event. Switch to mergeHooksSettings() so per-event matcher arrays are concatenated instead. Adds regression tests for mergeHooksSettings directly (exported for testing). Closes Gitlawb#1056
9826124 to
33b447f
Compare
|
Updated with the regression tests requested in review. Changes:
The test asserting that |
jatmn
left a comment
There was a problem hiding this comment.
Thanks for the update. The production fix still looks correct: using mergeHooksSettings() in the marketplace supplement path preserves the plugin.json hook matchers and appends marketplace hook matchers for the same event instead of overwriting them.
However, I do not think the requested change is complete yet.
Blocking
src/utils/plugins/pluginLoader.test.ts:22
The added coverage tests mergeHooksSettings() directly, but the requested regression test was for the loader behavior that actually regressed: loading a marketplace plugin where plugin.json defines, for example, hooks.PreToolUse, the marketplace entry also supplements hooks.PreToolUse, and the final loaded plugin has both matchers in order.
That distinction matters because the bug was not in the helper itself; it was that finishLoadingPluginFromPath() was not using the helper in the strict marketplace supplement path. A direct helper test can pass even if this loader path later regresses back to a spread or otherwise stops applying marketplace hook supplementation correctly.
Please add the focused loader-level regression test requested in the previous review. The direct mergeHooksSettings() unit tests are fine to keep, but they do not replace the integration coverage for finishLoadingPluginFromPath().
Validation
- Reviewed PR head
33b447f1f694d52fd5100f1593cb647d4b4d397b. - Confirmed
src/utils/plugins/pluginLoader.tsnow usesmergeHooksSettings()in the marketplace hook supplement path. - Confirmed the added tests only exercise
mergeHooksSettings()andmergePluginSources; they do not exercisefinishLoadingPluginFromPath()or the final loadedhooksConfig. - Ran
bun test src/utils/plugins/pluginLoader.test.ts: passed, 5 tests. - Ran
bun run typecheck: failed on broad existing repo-wide errors outside this PR-specific area, so it did not provide PR-specific signal.
Verdict: changes requested. The implementation is good, but the requested regression coverage has not been completed yet.
gnanam1990
left a comment
There was a problem hiding this comment.
Thanks for the update — the production change is still the right fix, and the new direct tests for mergeHooksSettings() are a nice addition. Re-reviewing against the new head 33b447f since my prior changes-requested was on 9826124.
Still blocking
The requested regression test was specifically at the loader level — exercising finishLoadingPluginFromPath with a marketplace plugin where plugin.json and the marketplace entry both supplement hooks for the same event, and asserting the resulting hooksConfig.PreToolUse contains both matchers in order.
The helper-level tests on mergeHooksSettings() lock in the helper's contract, but they do not protect against the actual regression that landed this bug: the loader path not calling the helper. If someone reverts the supplement path back to an object spread (or replaces it with any other merge that drops matchers), the helper unit tests still pass and the bug recurs silently.
A focused integration test using a tmp-dir fixture (write a plugin.json + a small marketplace entry, call into the load path, assert on the merged hooksConfig) is the coverage we need. Aligning with @jatmn's earlier comment on this point.
Non-blocking
- The inline comment in
pluginLoader.tsis good — clearly documents the invariant. - Exporting
mergeHooksSettingsfor direct testing is fine.
Once the loader-level test is in, this should be ready.
33b447f to
7ccff26
Compare
…merge Exports finishLoadingPluginFromPath so it can be tested directly. Adds a tmp-dir integration test that writes a plugin.json + hooks/hooks.json with a PreToolUse matcher, then runs finishLoadingPluginFromPath with a marketplace entry that also supplements PreToolUse. Asserts that hooksConfig.PreToolUse contains both matchers in order (plugin.json first, marketplace second), exercising the mergeHooksSettings() call path that was broken before this fix and could silently regress if reverted. Addresses reviewer feedback on PR Gitlawb#1056. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks for the detailed feedback. The loader-level integration test is now in place. What was added (head
If someone reverts the All 6 tests pass: |
jatmn
left a comment
There was a problem hiding this comment.
Changes requested.
The production change looks correct: the hasManifest && strict marketplace supplement path in finishLoadingPluginFromPath() now uses mergeHooksSettings() instead of object spread, so same-event hook matcher arrays from the plugin's own hook config and the marketplace entry are concatenated rather than overwritten.
Requested Fix
src/utils/plugins/pluginLoader.ts
Please fix the stale comment above mergeHooksSettings(). It currently says the helper is exported for tests of the marketplace supplement loader path and references createPluginFromPath:~2919, but the path under test is now finishLoadingPluginFromPath(). That comment should point at the actual loader function/path so future readers do not chase the wrong function while investigating this regression coverage.
My Prior Requested Changes
Completed.
Previous blocking request was to add loader-level regression coverage that:
- loads a marketplace plugin with a
plugin.json; - has the plugin's on-disk hook config define
hooks.PreToolUse; - has the marketplace entry supplement
hooks.PreToolUse; - asserts the final loaded
hooksConfig.PreToolUsecontains both matchers in order.
The current PR head includes this in src/utils/plugins/pluginLoader.test.ts under finishLoadingPluginFromPath - marketplace hook supplement. It creates a temp plugin directory, writes .claude-plugin/plugin.json plus hooks/hooks.json, calls finishLoadingPluginFromPath() with marketplace PreToolUse hooks, and asserts the loaded matchers are Bash then Write.
The earlier helper-only tests for mergeHooksSettings() are also present, but they are no longer the only coverage; the requested loader-level path is now exercised.
Review Notes
src/utils/plugins/pluginLoader.tsnow exportsmergeHooksSettings()andfinishLoadingPluginFromPath()to make the regression tests possible. These are not package-level public exports, so the practical API surface impact is limited to internal module imports.- The merge behavior preserves existing order: plugin/on-disk hooks remain first, marketplace-supplemented hooks are appended.
- The no-manifest marketplace-entry path still assigns
entry.hooksdirectly, which is consistent with the previous behavior and not part of the reported overwrite bug.
Validation
bun install --frozen-lockfile: passed.bun test src/utils/plugins/pluginLoader.test.ts: passed, 6 tests.bun run typecheck: failed on broad existing repo-wide baseline errors. I did not see PR-specific errors insrc/utils/plugins/pluginLoader.tsorsrc/utils/plugins/pluginLoader.test.ts.
|
Stale comment fixed in The comment above |
jatmn
left a comment
There was a problem hiding this comment.
Findings
src/utils/plugins/pluginLoader.test.ts:34still says the affected loader path iscreatePluginFromPath, but the bug and regression path under test are infinishLoadingPluginFromPath. I would update that comment so the test documentation matches the implementation and prior review discussion.
Completion Of My Requested Fixes
I confirmed my requested regression coverage has been added.
src/utils/plugins/pluginLoader.test.ts:136now includes a focusedfinishLoadingPluginFromPathtest that loads a plugin with existingPreToolUsehooks and a marketplace supplement for the same event.- The test asserts both matchers are present in order, with the plugin hook first and the marketplace hook second.
- The implementation change in
src/utils/plugins/pluginLoader.ts:2916now usesmergeHooksSettings(...)instead of object spread, which addresses the overwrite bug I called out.
|
Addressed in commit The stale @jatmn ready for re-review when you have a moment. |
please first clean the branch; it currently conflicts with main. |
When a plugin has both plugin.json and marketplace entry hooks for the same event, the old object spread silently overwrote the plugin.json matchers with the marketplace entry's matchers. mergeHooksSettings() already exists in this file and correctly concatenates per-event matcher arrays — it was used in createPluginFromPath but not in the marketplace supplement path. Affected path: finishLoadingPluginFromPath (has-manifest + strict mode)
…e path The fix in the previous commit used mergeHooksSettings() instead of object spread in the marketplace supplement path (createPluginFromPath:~2919). These tests validate the key invariant: same-event hook arrays from plugin.json and from the marketplace entry must be concatenated, not overwritten. Also exports mergeHooksSettings for direct testing as requested by reviewers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…merge Exports finishLoadingPluginFromPath so it can be tested directly. Adds a tmp-dir integration test that writes a plugin.json + hooks/hooks.json with a PreToolUse matcher, then runs finishLoadingPluginFromPath with a marketplace entry that also supplements PreToolUse. Asserts that hooksConfig.PreToolUse contains both matchers in order (plugin.json first, marketplace second), exercising the mergeHooksSettings() call path that was broken before this fix and could silently regress if reverted. Addresses reviewer feedback on PR Gitlawb#1056. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The explicit parameter annotation was narrower than the actual HooksSettings union type, causing a TS2345 type error. Moved the cast to the array itself so TypeScript can infer the callback parameter type correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment The comment referenced createPluginFromPath but the marketplace hook supplement block lives in finishLoadingPluginFromPath (extracted later). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…omment Matches the fix already applied to pluginLoader.ts: the loader path under test is finishLoadingPluginFromPath, not createPluginFromPath. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
a1073fd to
73d6d36
Compare
CI failure analysisThe 6 failing tests are not caused by changes in this PR. Same pre-existing test isolation issue also seen in #1059 (opened simultaneously). All 4 A test using prefix These failures are reproducible on any branch that was rebased onto today's |
|
Update (2026-05-14): Same 6 tests failing again on today's CI run — and also on PR #1059 and PR #1076 (three different branches, same list). This is a confirmed repo-wide test suite issue unrelated to any branch content:
2538 other tests pass. The PR changes ( |
|
please update with main and try again , smoke issue should be resolved. |
|
@jatmn the stale comment (pointing to a non-existent line) was removed in commit |
BlockersThis is a duplicate of #1167 which has already been approved and merged. Looks Good
Verdict: Close as duplicate — #1167 already covers this fix. |
|
Duplicate of #1167. |
Fixes #1055
Summary
finishLoadingPluginFromPath, the marketplace hook supplement path used an object spread that overwrote per-event matcher arrays fromplugin.jsonwith those from the marketplace entrymergeHooksSettings()already exists in the same file and concatenates arrays correctly — it was used increatePluginFromPathbut not in this supplement pathmergeHooksSettings()(3-line change)What changed
src/utils/plugins/pluginLoader.ts— supplement hooks path infinishLoadingPluginFromPath:Test plan
plugin.jsonhooks + marketplace supplement for same event → both matchers present after loadplugin.jsonhooks, no marketplace supplement → no change in behaviorplugin.json→mergeHooksSettings(undefined, hooks)returnshooks(identical to old assignment)