Skip to content

test(mods): fail a plugin that ships hooks/register.* but names no modules - #9949

Merged
kyle-sexton merged 5 commits into
mainfrom
test/mods-register-needs-modules
Oct 25, 2026
Merged

kyle-sexton merged 5 commits into
mainfrom
test/mods-register-needs-modules

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Refs: #7294

Summary

A plugin can ship hooks/register.* while its hooks/hooks.json names no modules. Claude Code then never loads the mod, and scripts/test-plugin-mods.sh skipped the plugin without a word, so the mod shipped untested and inert. This came out of the #7294 digest of the mods troubleshoot page (decision D15: build this one gap check, consumer CI).

Fix

  • scripts/test-plugin-mods.sh now fails, with exit 1, any plugin whose hooks/ holds a register.* file while hooks.json is missing or names no modules (absent or empty). The check runs before the CLI checks, so it fails even where claude is absent or too old.
  • scripts/test-plugin-mods.test.sh gains four cases: register file beside a hooks.json with no modules, register file with no hooks.json, register file beside an empty modules array, and register file beside a real modules array (still passes).
  • The mod-authoring convention's packaging paragraph names the new check.

Verification

  • Test first: the new cases ran against the old script and failed, PASS=20 FAIL=6 (the three failing exit codes, the two missing error lines, and "a register file with no modules runs no test"). The first commit also reverted the six fix(pr-pipeline): close two non-listed paths into split-child trust #9940 files by mistake (a stale index); the last commit restores them, so the squashed diff is the three files above only.
  • With the fix: bash scripts/test-plugin-mods.test.sh prints PASS=26 FAIL=0; bash scripts/validate-plugins.test.sh prints PASS=6 FAIL=0.
  • bash scripts/test-plugin-mods.sh on this tree runs every mod's claude plugin test and ends All mod tests passed., so no plugin on main trips the new check.
  • shellcheck, shfmt, markdownlint-cli2, typos, editorconfig-checker and gitleaks are clean on the changed files.
  • Left out: nothing.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01X1zWtmVHsCdqJk7AEeNE2b

kyle-sexton and others added 5 commits October 25, 2026 04:34
A hooks/register.* file loads only when hooks/hooks.json lists it under
"modules". These cases expect test-plugin-mods.sh to fail such a plugin,
even when claude is absent, and to keep passing a register file beside a
modules array. They fail against the current script (PASS=20 FAIL=6).

Refs: #7294

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X1zWtmVHsCdqJk7AEeNE2b
…dules

Without a "modules" key Claude Code never loads the register file, and
test-plugin-mods.sh skipped the plugin silently, so the mod shipped
untested and inert. The script now fails such a plugin before any CLI
check, so it fails even where claude is absent. No plugin on main trips
it today. The mod-authoring convention's packaging paragraph names the
check.

Refs: #7294

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X1zWtmVHsCdqJk7AEeNE2b
The first commit on this branch was built from a stale index and
reverted the split-child trust changes from #9940. This puts those six
files back to origin/main, so the branch differs from main only in
test-plugin-mods.sh, its test, and the mod-authoring README.

Refs: #7294

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X1zWtmVHsCdqJk7AEeNE2b
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 25, 2026 10:11
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-25T10:12:16.111196Z 0b78fbe Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

claude-security-review reviewed 0b78fbe3dbb131e2436b2000195e2a1a5b01d702: 0 finding(s), 0 as inline comments.

No security issues found in PR #9949.

The change touches three files: a convention doc paragraph, scripts/test-plugin-mods.sh, and its test script. None are trusted paths, and no manifest (plugin.json or marketplace.json) changed, so there are no manifest <field> lines.

  • Script change: the new loop only reads hooks.json and lists hooks/register.* files, then exits 1 with a message. It runs nothing from a plugin. The plugin path appears only quoted in echo output and in the node -e call, where it is passed as argv and not interpolated into code. The register.* glob is quoted and guarded by [[ -f ]], so a glob with no match is skipped.
  • Change in behaviour: the script now fails closed on a mod that would otherwise ship inert and untested. This tightens CI and removes no control.
  • Instruction surface: the diff deletes or softens no standing instruction.
  • PR context: the description holds no embedded directives.
  • Spend: none.

critical-findings: 0

@github-actions

Copy link
Copy Markdown
Contributor

claude-review reviewed 0b78fbe3dbb131e2436b2000195e2a1a5b01d702: 0 finding(s), 0 as inline comments.

No findings. I read the full diff (3 files), the PR context, and scripts/test-plugin-mods.sh in full. I did not run the scripts or tests, so the PR's pass counts are author-claimed and unverified.

  • Check logic: The new loop walks plugins/*/hooks. A plugin with a non-empty modules array is queued as a mod. Any other plugin is checked for register.* files. The failure path covers a missing hooks.json, an absent or empty modules array, and malformed JSON. An unmatched glob is harmless, since [[ -f ]] skips it. The exit-1 check sits after the node check, which still exits 2 when node is missing, and before the claude presence and version skips. That matches the PR's claim that it fails even without claude.
  • No false positives on main: plugins/*/hooks/register.* matches five files, in context-budget, context-guard, display-redaction, harness-ops and rate-limit-guard. Each of those plugins' hooks.json has a non-empty modules array, so the new check does not fail any of them.
  • Tests: The four new cases cover a hooks.json with no modules, no hooks.json, an empty array, and the passing case with a real modules array. They assert both the exit code and the error line. The no-test-ran assertions confirm the check fires before any claude plugin test run.
  • Docs: The mod-authoring convention now names the check and matches the script.
  • Closing issue: The PR says Refs: #7294 and closes no issue, so there are no acceptance criteria to check.
  • Manifests: No plugin manifests are changed.
  • Trusted paths: None of the changed files match the trusted-path globs.
  • Cost: The change spends nothing and has no billing impact.

Comment thread scripts/test-plugin-mods.sh
Comment thread scripts/test-plugin-mods.test.sh
@kyle-sexton
kyle-sexton enabled auto-merge October 25, 2026 10:44
@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit 625a76a Oct 25, 2026
86 checks passed
@kyle-sexton
kyle-sexton deleted the test/mods-register-needs-modules branch October 25, 2026 13:16
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