Skip to content

PluginConfigValidator can never run: PluginLoader calls its own validatePluginConfig(metadata) with no config, and a plugin factory closes over its config so the kernel never receives it #11982

Description

@os-zhuang

Found while implementing #11637 (the REST server config seam). Filed, not fixed — #11637's declared surface is packages/rest/src/rest-server.ts, and this is a packages/core mechanism with its own gate family.

#11637 offered "declare configSchema on the REST plugin and let the kernel's plugin-config-validator do it" as one of its two candidate fixes. Measuring that candidate is what turned this up: the mechanism cannot run at all, for any plugin, so the candidate was structurally unavailable and #11637 landed the seam-side parse instead.

What was measured

On origin/main @ 7899f5745.

1. The one call site passes no config.packages/core/src/plugin-loader.ts:157-159:

if(metadata.configSchema){this.validatePluginConfig(metadata);// <- no second argument}

and :406-419:

privatevalidatePluginConfig(plugin: PluginMetadata,config?: any): void{if(!plugin.configSchema)return;if(config===undefined){// In loadPlugin, we often don't have the config yet.// We skip validation here or valid against empty object if schema allows?// For now, let's keep the logging behavior but note it's delegatingthis.logger.debug(`Plugin ${plugin.name} has configuration schema (config validation postponed)`);return;}this.configValidator.validatePluginConfig(plugin,config);}

config is always undefined here, so the early return always fires. "Postponed" to nothing: git grep validatePluginConfig finds no other caller outside PluginConfigValidator's own unit test.

2. There is nothing for it to postpone to.kernel.use(plugin) (packages/core/src/kernel.ts:198) hands pluginLoader.loadPlugin(plugin) the Plugin object only. A plugin factory captures its config in a closure — createRestApiPlugin(config) is the model — so no config value ever crosses into the kernel, and PluginMetadata carries no field for one. Even a correct validatePluginConfig(metadata, config) call would have nothing to pass.

3. No kernel plugin in the repo declares configSchema.git grep -n "configSchema:" packages -- ':!**/dist/**' ':!**/CHANGELOG.md' returns 20 hits and none is a kernel Plugin: they are automation node-executor JSON schemas (service-automation/src/builtin/*), the datasource driver catalog, plugin-approvals' approval-node descriptor, and spec/test declarations. So PluginConfigValidator — ~200 lines with its own unit test, its own formatZodErrors, validatePartialConfig and getDefaultConfig — has zero live consumers.

Why it matters

PluginMetadata.configSchema reads, at every author's call site, as the declared way to validate a plugin's configuration. It is documented as such (packages/core/ADVANCED_FEATURES.md:304, ADR-0025's "Config" section: "PluginConfigValidator validates plugin config against the plugin's schema"). A plugin author who declares one gets a debug line and no validation — the declared-not-enforced shape, one layer up from the one #11637 closes.

Not prejudged

Three shapes, and the choice is a real call:

Worth measuring before choosing: whether ADR-0025's manifest-driven load path ("configuration" in the plugin manifest) has a live call site that would have a config in hand, since that is the one caller for which the mechanism was designed.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions