Skip to content

Reject Claude manifest hooks refs to the auto-loaded hooks/hooks.json - #113

Merged
mike-north merged 3 commits into
mainfrom
claude/hooks-json-autoload-guard
Aug 27, 2026
Merged

mike-north merged 3 commits into
mainfrom
claude/hooks-json-autoload-guard

Conversation

@mike-north

Copy link
Copy Markdown
Contributor

Claude Code auto-loads a plugin's hooks/hooks.json and refuses to load any plugin whose .claude-plugin/plugin.json hooks field also names that file:

Duplicate hooks file detected … The standard hooks/hooks.json is loaded automatically,
so manifest.hooks should only reference additional hook files.

This shipped as a real end-user failure in a downstream marketplace plugin (mike-north/ofocus#93 fixed the instance), so aipm validate should catch the pattern before publish.

What changed

  • New validation rule (packages/core/src/targets/claude/validate.ts): a hard schema-invalid finding when the manifest's hooks field — a string, or any string entry when it's an array — normalizes to the plugin-relative hooks/hooks.json. The check reads the raw manifest JSON rather than the schema-parsed value because claudePluginManifestSchema models hooks as string | Record, while Claude accepts string | array | object; the array form would otherwise be invisible. The rule targets the reference, not the file — carrying hooks/hooks.json as the shared Codex/Gemini artifact with no manifest ref draws no finding.
  • Spec (docs/specs/architecture.md, 0.4.2 → 0.4.3): §10.1 gains a fifth validator category, host-contract checks, with §10.1.5 as its first normative rule; §10.2 records host-contract violations as hard.
  • Tests: six regression cases in validate.test.ts (firing: plain ref, hooks/./hooks.json normalization, array form; non-firing: claude.json ref, auto-loaded file with no manifest ref, similarly-named file), with the Claude docs contract and observed error text cited in the file header.
  • Decision doc (docs/specs/hooks-json-autoload-collision.md, Draft): the guard papers over a deeper design conflict — Claude's auto-load vs. hooks/hooks.json as the shared Codex/Gemini artifact (adapter-system.md D6). Notably, the in-plugin-dir Gemini hooks/hooks.json is read by nobody today (the Gemini bundler regenerates it into dist/ independently), so a Gemini-format file can be silently auto-loaded as Claude hooks for no benefit. The doc lays out affected target combinations and resolution options with a staged recommendation; nothing in it is implemented here.

Changeset included (minor, @ai-plugin-marketplace/core).

Note: targets/kiro/bundle.test.ts and targets/gemini/bundle.test.ts (template-repo parity) fail identically on main; they're unrelated to this change.

Copilot AI lite review requested due to automatic review settings August 27, 2026 17:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a Claude-target “host-contract” validation rule to prevent a real-world plugin load failure: Claude Code auto-loads hooks/hooks.json and errors if the manifest’s hooks field also references that same file. The change updates the architecture spec to document the new validator category and adds regression coverage plus a decision doc describing the underlying design collision.

Changes:

  • Add a Claude validator that hard-fails when .claude-plugin/plugin.json hooks references hooks/hooks.json (including normalized variants and array entries).
  • Add targeted regression tests covering firing/non-firing cases for the duplicate-reference rule.
  • Update the architecture spec to define “host-contract checks” and document the new Claude rule; add a draft decision document and a changeset entry.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/core/src/targets/claude/validate.ts Adds a host-contract validation that rejects manifest refs to Claude’s auto-loaded hooks/hooks.json.
packages/core/src/targets/claude/validate.test.ts Adds regression tests for the new duplicate hooks reference rule.
docs/specs/hooks-json-autoload-collision.md Adds a draft decision document describing the broader hooks artifact collision and resolution options.
docs/specs/architecture.md Updates spec version and documents the new host-contract validator category and rule §10.1.5.
.changeset/curly-poems-repeat.md Publishes the behavior change in @ai-plugin-marketplace/core as a minor release note.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/core/src/targets/claude/validate.ts
Comment thread packages/core/src/targets/claude/validate.test.ts Outdated
Comment thread docs/specs/architecture.md Outdated
…ks/hooks.json

Claude Code auto-loads <pluginDir>/hooks/hooks.json and hard-errors when the
plugin manifest's `hooks` field names that same file ("Duplicate hooks file
detected ... The standard hooks/hooks.json is loaded automatically, so
manifest.hooks should only reference additional hook files").

`aipm validate` now emits a hard `schema-invalid` finding when the Claude
manifest's `hooks` field -- a string, or any string entry when it is an array --
normalizes to the plugin-relative path `hooks/hooks.json`. The check reads the
raw manifest rather than the schema-parsed value, so the array form (legal in
Claude Code, unmodeled by claudePluginManifestSchema) is still caught.

architecture.md gains §10.1 category 5 (host-contract checks) and §10.1.5
documenting the rule, and notes the still-open collision between Claude's
auto-load and the shared Codex/Gemini hooks/hooks.json artifact.
@mike-north
mike-north force-pushed the claude/hooks-json-autoload-guard branch from 63ee30d to 3314fdb Compare August 27, 2026 18:14
@mike-north
mike-north merged commit 5e45ad5 into main Aug 27, 2026
2 checks passed
@mike-north
mike-north deleted the claude/hooks-json-autoload-guard branch August 27, 2026 18:25
@github-actions github-actions Bot mentioned this pull request Aug 27, 2026
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.

2 participants