Skip to content

fix(plugins): Make plugin loading work end to end - #1386

Open
TimothyJones wants to merge 4 commits into
claude/plugin-docs-update-ck8mgsfrom
claude/plugin-loading
Open

fix(plugins): Make plugin loading work end to end#1386
TimothyJones wants to merge 4 commits into
claude/plugin-docs-update-ck8mgsfrom
claude/plugin-loading

Conversation

@TimothyJones

@TimothyJones TimothyJones commented Aug 15, 2026

Copy link
Copy Markdown
Member

Stacked on #1385 (claude/plugin-docs-update-ck8mgs) — fixes three of the plugin todos raised while writing the plugin documentation, plus a bug found along the way.

Module namespace unwrapping

A dynamic import() namespace never exposes description etc for plugins built with export default or module.exports = plugin (only individual named exports worked), so the documented "default export one ContractCasePlugin object" shape could never actually load. New mustResolvePlugin / isContractCasePlugin helpers in case-plugin-base accept every packaging style (ESM default, CJS module.exports, TypeScript-compiled CJS default, named exports), with a new documented INVALID_PLUGIN_MODULE error code for modules that aren't plugins. PluginLoader's TODO: Validate plugins here is implemented with the same guard.

Project-local resolution (the Java case)

Plugin names are now resolved against the working directory first, falling back to normal resolution. This is what makes plugins findable when the connector runs from a temporary directory, as it does for Java. There's also a require() fallback for environments where dynamic import() isn't available — notably Jest without --experimental-vm-modules, which TS DSL users would otherwise hit.

Additionally, the specifier validation moved inside the promise chain: previously an invalid name threw synchronously out of the never-throws boundary instead of producing a BoundaryFailure.

loadPlugins in the TS DSL

ContractCaseDefiner.loadPlugins(...) and ContractVerifier.loadPlugins(...), mirroring the Java DSL semantics (DEFINER_LOAD_PLUGIN / VERIFICATION_LOAD_PLUGIN). BoundaryPluginLoader is now exported from case-connector to support this. No adapter mirroring needed — the jest/vitest adapters re-export the shared classes.

Java definer bug

InternalDefinerClient.loadPlugins built its request by calling .addAll() on the protobuf builder's unmodifiable getModuleNamesList() view, so the definer never actually sent the module names. Fixed to .addAllModuleNames(...), matching the verifier. The stale javadoc ("Can be a path to the package") is also corrected — paths are intentionally rejected.

Tests

  • 17 new case-connector vitest tests over a committed fixture plugin package (test-fixtures/contract-case-test-plugin) with one entry point per packaging style, covering load success, idempotency, non-plugins, missing packages, and unsafe specifiers
  • Jest adapter define + verify e2e specs that load the fixture by name and use its matcher in a real interaction (contract committed, as usual)
  • PluginLoadTest in dsl-java: the full chain — Java → gRPC → temp-dir connector → project node_modules → unwrap → matcher registered and matched — plus a negative test. The full Java suite passes.

The user-facing docs from the base PR are updated in place (TS loadPlugins examples added, "not yet supported" caveat removed), and the corresponding maintainer todos are ticked.

🤖 Generated with Claude Code

https://claude.ai/code/session_014mD6p9EEmftbpBQHZZBWB1

claude added 4 commits August 13, 2026 03:17
…e packaged

Previously, loading a plugin by name failed for plugins built with
`export default plugin` or `module.exports = plugin`, because the dynamic
import()'s module namespace never exposes the plugin's properties directly.
Now the module contents are unwrapped with the new mustResolvePlugin
helper (in case-plugin-base), which accepts every packaging style and
throws a helpful INVALID_PLUGIN_MODULE configuration error when a module
doesn't contain a plugin.

Additionally:

- Plugin names are now resolved against the working directory (the user's
  project) first, so plugins are found when the connector runs from a
  temporary directory (eg when called from the Java DSL)
- Falls back to require() in environments that can't do a dynamic import()
  (eg Jest without --experimental-vm-modules)
- PluginLoader now validates the plugin objects it's given (previously a
  TODO)
- Unsafe module specifiers now surface as a BoundaryResult failure rather
  than a synchronous throw across the never-throws boundary

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mD6p9EEmftbpBQHZZBWB1
…fier

Mirrors the Java DSL's loadPlugins methods, so that JS/TS users can load
plugins too. Includes end-to-end tests that load a fixture plugin by
package name and use its matcher during contract definition and
verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mD6p9EEmftbpBQHZZBWB1
…s it

Adds TypeScript examples to the loading documentation, removes the
caveat about the JS DSL not exposing loadPlugins, and ticks off the
plugin loading items in the maintainer todo list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mD6p9EEmftbpBQHZZBWB1
… definer

The definer's loadPlugins built its request by calling .addAll() on the
protobuf builder's unmodifiable module names list view, so the module
names were never sent to the connector. Now uses .addAllModuleNames(),
matching the verifier.

Also adds an end-to-end test that loads a fixture plugin by package name,
proving that plugins are resolved from the project's node_modules even
though the connector runs from a temporary directory - and corrects the
loadPlugins javadoc, which claimed paths were supported (they aren't, for
security reasons).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mD6p9EEmftbpBQHZZBWB1
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