Skip to content

Commit 49f0dcf

Browse files
claude[bot]claude
andauthored
feat(core): retire the inert PluginMetadata surfaces — configSchema with PluginConfigValidator, and hotReloadable (ADR-0049) (#12689)
* feat(core): retire PluginMetadata.configSchema and PluginConfigValidator under ADR-0049 (#11982) The kernel-owned plugin-config-validation surface could never run: the loader's one call site passed no config, plugin factories close over their config so the kernel never receives it, and zero plugins declared a configSchema (measured with positive controls; maintainer ruled Option B, 2026-08-27, decision-inbox batch 5). - remove PluginMetadata.configSchema and the always-early-returning validatePluginConfig path from PluginLoader - delete PluginConfigValidator / createPluginConfigValidator and their unit test; unpublish them from the security barrel - record the retirement in ADR-0025 section 3.7: re-declaring a kernel-owned config-validation surface is a fresh decision for the day the distribution layer lands, with the zero-caller measurement as starting evidence - drop the ADVANCED_FEATURES.md section whose example promised 'Config is validated before init is called' — false on this ref - pin the retirement: barrel no longer exports the validator (runtime), a declared configSchema no longer type-checks (compile-time, via the type-check DEBT ratchet), startupTimeout as the live-sibling positive control Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry * feat(core): retire PluginMetadata.hotReloadable under ADR-0049 (#12587) Declared 'Whether plugin supports hot reload' and documented, with zero reads and zero declarations: HotReloadManager.reloadPlugin gates only on its own registered reload configs, so hotReloadable: false was hot-reloaded identically to true — a reload-safety assurance the runtime never honoured (maintainer ruled Option B, 2026-08-27, decision-inbox batch 5, same batch as the configSchema retirement). - remove PluginMetadata.hotReloadable and its ADVANCED_FEATURES.md line - drop the field from ADR-0025's present-capability inventory (the section 3.7 record already names this sibling retirement; the ADR's distribution-layer design prose keeps its forward-looking mentions) - pin: a declared hotReloadable no longer type-checks (compile-time, via the type-check DEBT ratchet) - add the family changeset covering both retirements (@objectstack/core minor under the lockstep launch-window convention, with the ADR-0087 runtime-interface-only disposition) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry * test(core,rest): host the retirement compile pins in a compiled program (#11982, #12587) check:type-check-coverage correctly refused the first shape: @objectstack/core has no typecheck script (type-check DEBT ledger entry), so a @ts-expect-error in core is a phantom pin no tsc program a typecheck script runs would evaluate, and PHANTOM_PIN_DEBT is closed to new entries. - core keeps the RUNTIME pins (security barrel no longer publishes PluginConfigValidator / createPluginConfigValidator, live-sibling positive control on the namespace) - the COMPILE-TIME pins move to packages/rest, whose tsconfig.test.json program is run by its typecheck script (check:test-typecheck, EXACT per-file ratchet) and resolves @objectstack/core to the BUILT dist .d.ts — so the directives pin the published contract consumers actually see, in the package that carries the retirement's worked replacement (#11637 seam parse) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry * docs: align the hand-written plugin docs with the ADR-0049 retirements (#11982, #12587) Docs-drift pass over the six pages the drift bot anchored to this diff: - plugins/anatomy.mdx: the Plugin-class example carried the retired configSchema field with a comment claiming the loader validates it; the block now shows constructor-owned config and names the retirement - plugins/index.mdx: the Configuration Validation bullet and the configSchema half of the securePlugin example removed (signature kept - live surface); replacement paragraph states the plugin-owned self-parse pattern - protocol/kernel/index.mdx: the Configuration Management snippet and the fail-fast callout no longer document the retired field as merely 'postponed' - both now state the retirement and the self-parse seam No change, with the reason measured per page: automation/flows.mdx names the ADR-0018 node-executor configSchema (different surface); getting-started/quick-reference.mdx names spec's plugin-validator.zod.ts PluginMetadata (locally-declared homonym, live); protocol/kernel/plugin-spec.mdx teaches the surviving self-parse pattern - its phantom manifest file-map row predates this diff and is filed as #12690. content/docs/releases/v17.mdx is release-owned and untouched; its four configSchema mentions are all the ADR-0018 / driver surfaces, none the kernel field. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 96732b0 commit 49f0dcf

12 files changed

Lines changed: 212 additions & 568 deletions
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
"@objectstack/core": minor
3+
---
4+
5+
feat(core): retire the inert `PluginMetadata` surfaces — `configSchema` with `PluginConfigValidator`, and `hotReloadable` (#11982, #12587, ADR-0049)
6+
7+
<!-- adr-0087: not-required (runtime-interface-only packages/core/src/plugin-loader.ts#PluginMetadata) PluginMetadata is a runtime TS interface in packages/core with no Zod schema, no spec declaration and no stored representation; no metadata surface references it (the PluginMetadata in packages/spec/src/kernel/plugin-validator.zod.ts is an unrelated locally-declared homonym). The deleted PluginConfigValidator / createPluginConfigValidator were runtime classes in the same non-metadata module family, so `objectstack migrate meta` has nothing to rewrite; the compiler is the notification channel — TS2353 on the removed fields, TS2305 on the removed exports. -->
8+
9+
**BREAKING**: removes a published-but-inert capability from the `.` entry of
10+
`@objectstack/core`. Shipped as `minor` under the lockstep launch-window
11+
convention (a `major` bump is refused repo-wide by `check:changeset-no-major`).
12+
13+
Removed, each measured at zero live consumers with positive controls (the
14+
sibling `startupTimeout` is read live by the kernel's startup timeout guard);
15+
maintainer ruled retire under ADR-0049 enforce-or-remove, 2026-08-27,
16+
decision-inbox batch 5; recorded in ADR-0025 §3.7:
17+
18+
- `PluginMetadata.configSchema` — declared "Configuration schema for
19+
validation", but the mechanism could never run: the loader's only call
20+
passed no config, and no caller could — plugin factories close over their
21+
config, so the kernel never receives it. Every one of ~40 production
22+
`kernel.use()` compositions already passes config as constructor arguments
23+
and works.
24+
- `PluginConfigValidator` / `createPluginConfigValidator` — the validator
25+
behind that field: real code with zero reachable invocations, deleted along
26+
with its unit test and its export from the security barrel.
27+
- `PluginMetadata.hotReloadable` — declared "Whether plugin supports hot
28+
reload" with zero reads and zero declarations: `HotReloadManager.reloadPlugin`
29+
gates only on its own registered reload configs, so `hotReloadable: false`
30+
was hot-reloaded identically to `true`.
31+
- The `packages/core/ADVANCED_FEATURES.md` example whose inline comment
32+
promised "Config is validated before init is called" — false on the
33+
retired ref, and the retired surface's only in-repo declaration site.
34+
35+
One-line fixes, per symbol. If you declared `configSchema` on a plugin:
36+
delete the field and parse your config at the plugin's own seam —
37+
`MyConfigSchema.parse(options)` in the plugin factory or constructor, the
38+
pattern `packages/rest` uses. If you imported `PluginConfigValidator` or
39+
`createPluginConfigValidator`: delete the import and hold your own
40+
`schema.parse` call; the compiler (TS2305) locates every such site. If you
41+
declared `hotReloadable`: delete the field — it never gated anything, and
42+
hot-reload participation remains governed solely by
43+
`HotReloadManager.registerReloadConfig`.
44+
45+
Re-declaring a kernel-owned config-validation surface is a fresh decision for
46+
the day ADR-0025's plugin distribution layer lands, with #11982's zero-caller
47+
measurement as its starting evidence.

content/docs/plugins/anatomy.mdx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Plugins are the building blocks of ObjectStack. A plugin is a plain JavaScript/T
99

1010
```typescript
1111
import type { Plugin, PluginContext } from '@objectstack/core';
12-
import { z } from 'zod';
1312

1413
export class MyPlugin implements Plugin {
1514
// Identity
@@ -21,12 +20,12 @@ export class MyPlugin implements Plugin {
2120
// This controls init ordering — it is NOT an npm-style version map.
2221
dependencies = ['com.objectstack.engine.objectql'];
2322

24-
// Configuration Schema (Optional)
25-
// Read by the plugin loader to validate config; it lives on the plugin
26-
// metadata rather than the base `Plugin` interface.
27-
configSchema = z.object({
28-
apiKey: z.string()
29-
});
23+
// Configuration (Optional)
24+
// A plugin owns its config: take it as a constructor/factory argument and
25+
// parse it yourself (with a Zod schema, say) before use. The kernel never
26+
// receives plugin config — the old `configSchema` metadata field was
27+
// retired under ADR-0049 because nothing could ever run it.
28+
constructor(private readonly options: { apiKey?: string } = {}) {}
3029

3130
/**
3231
* Init Phase (REQUIRED)

content/docs/plugins/index.mdx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,22 +284,21 @@ const dbPlugin: PluginMetadata = {
284284

285285
ObjectStack supports plugin security features:
286286

287-
- **Configuration Validation**: Plugins can define a Zod `configSchema` for runtime validation
288287
- **Signature Verification**: Cryptographic signatures for plugin integrity
289288
- **Permission Enforcement**: Fine-grained access control for plugin operations
290289

291-
```typescript
292-
import { z } from 'zod';
290+
Plugin configuration is the plugin's own concern: take it as a constructor or
291+
factory argument and parse it at your own seam (a Zod `schema.parse` in the
292+
factory, constructor or `init`). The kernel-side `configSchema` metadata field
293+
was retired under ADR-0049 — the kernel never received a plugin's config, so
294+
the field could not validate anything.
293295

296+
```typescript
294297
const securePlugin: PluginMetadata = {
295298
name: 'com.example.secure',
296299
version: '1.0.0',
297-
configSchema: z.object({
298-
apiKey: z.string().min(1),
299-
region: z.enum(['us', 'eu', 'ap']),
300-
}),
301300
signature: 'ed25519:key-1:<base64url-signature>',
302-
301+
303302
async init(ctx) { /* ... */ },
304303
};
305304
```

content/docs/protocol/kernel/index.mdx

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -403,20 +403,14 @@ packages/plugins/plugin-slack-integration/
403403
404404
### Configuration Management
405405
```typescript
406-
// A plugin object may carry a Zod `configSchema` describing its settings.
407-
// NOTE: the kernel RECORDS the schema but does not enforce it yet — `use()`
408-
// takes no config argument, so the loader has nothing to parse and logs
409-
// "config validation postponed" instead of running the schema. Treat
410-
// `configSchema` as a declaration of shape, and validate values you actually
411-
// depend on yourself.
406+
// A plugin owns its config: `use()` takes no config argument and the kernel
407+
// never receives one, so validate the values you depend on at your own seam
408+
// (a Zod `schema.parse` in the plugin's factory, constructor or `init`).
409+
// The old `configSchema` metadata field was retired under ADR-0049 — the
410+
// loader only ever logged "config validation postponed" and returned.
412411
export const slackPlugin: Plugin = {
413412
name: 'slack-integration',
414413
version: '0.1.0',
415-
configSchema: z.object({
416-
apiKey: z.string().describe('Slack API Key'),
417-
channel: z.string().default('#general'),
418-
enabled: z.boolean().default(true),
419-
}),
420414
async init(ctx) {
421415
// Runtime-resolved values come from the `settings` service, not from a
422416
// `config` object on the context — there is no `ctx.config`.
@@ -487,10 +481,11 @@ const message = i18n.t('slack.button.send', context.locale);
487481
**Example:** Wiring, not config, is what the kernel currently enforces at boot. A plugin that declares a dependency the kernel never received fails `bootstrap()` outright — `[Kernel] Dependency 'com.objectstack.engine.objectql' not found for plugin 'com.objectstack.audit'` — and a dependency cycle throws `[Kernel] Circular dependency detected: <plugin>`. Boot stops there instead of a request failing later.
488482
489483
<Callout type="warn">
490-
Plugin `configSchema` is **not** part of this fail-fast path yet. The loader
491-
stores the schema and postpones the check, so a missing or malformed value in
492-
a plugin's config will not stop boot today. Validate config you depend on in
493-
your own `init`.
484+
Plugin config is **not** part of this fail-fast path. The old `configSchema`
485+
metadata field was retired under ADR-0049 — the loader never enforced it —
486+
so a missing or malformed value in a plugin's config will not stop boot.
487+
Parse config you depend on yourself, in your plugin's factory, constructor
488+
or `init`.
494489
</Callout>
495490
496491
## Comparison: Kernel vs Alternatives

docs/adr/0025-plugin-package-distribution.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ code and npm dependencies**, not just metadata. The repository already has the
5757
schema), and a `src/index.ts` with a lifecycle entry point.
5858
- The microkernel can already *load* code plugins: `packages/core/src/
5959
plugin-loader.ts` (dependency ordering, health checks, `signature` field,
60-
`startupTimeout`, `hotReloadable`), `packages/core/src/types.ts` (`Plugin`
60+
`startupTimeout`), `packages/core/src/types.ts` (`Plugin`
6161
with `init/start/destroy` + `PluginContext`),
6262
`packages/core/src/security/plugin-permission-enforcer.ts`
6363
(capability-based service/hook/file/network enforcement),
64-
`PluginConfigValidator`, and `packages/runtime/src/sandbox/quickjs-runner.ts`
64+
and `packages/runtime/src/sandbox/quickjs-runner.ts`
6565
(a QuickJS-WASM sandbox that wires only capability-gated `ctx.api/crypto/log`
6666
into untrusted code).
6767

@@ -149,7 +149,7 @@ Extends the existing `ObjectStackManifest` with three new blocks
149149
"fs": []
150150
},
151151
"integrity": { "dist/index.mjs": "sha256-..." }, // per-file hashes
152-
"configuration": { /* existing config schema (PluginConfigValidator) */ },
152+
"configuration": { /* config schema — validator retired, re-decide with this layer (§3.7) */ },
153153
"capabilities": { /* existing implements/provides/requires/contributes */ },
154154
"contributes": { /* OPTIONAL declarative metadata: objects/views/flows/... */ }
155155
}
@@ -276,8 +276,18 @@ enforces this at publish time (an unverified publisher cannot ship `runtime:
276276
granted set → `PluginPermissionEnforcer` (service/hook/file/network already
277277
enforced). Principle of least privilege; all denials logged (existing
278278
behavior).
279-
- **Config.** `PluginConfigValidator` validates plugin config against the
280-
`configuration` schema.
279+
- **Config.** RETIRED 2026-08-27 (#11982, ADR-0049 enforce-or-remove;
280+
maintainer ruling, decision-inbox batch 5). `PluginConfigValidator` /
281+
`createPluginConfigValidator` and `PluginMetadata.configSchema` were removed:
282+
the mechanism could never run — the loader's one call site passed no config,
283+
no manifest→`loadPlugin` path existed to carry one, `PluginMetadata` had no
284+
config-value field, and zero plugins declared a schema (measured with
285+
positive controls on #11982; the sibling `hotReloadable` fell to the same
286+
measurement in #12587). Re-declaring a kernel-owned config-validation
287+
surface is a **fresh decision** for the day this distribution layer actually
288+
lands, with #11982's zero-caller measurement as its starting evidence — the
289+
manifest `configuration` block below records the design intent, not a live
290+
validator.
281291
- **Supply chain.** Lockfile + per-file `integrity`; server-side scan for
282292
secrets and known-vuln deps; SBOM stored on the version row; **always**
283293
`--ignore-scripts` (no `postinstall`).
@@ -496,7 +506,6 @@ the developers and operators who compose Apps and provision runtimes.
496506
- `packages/core/src/plugin-loader.ts` — plugin loading, lifecycle, health, signature
497507
- `packages/core/src/types.ts``Plugin` (`init/start/destroy`) + `PluginContext`
498508
- `packages/core/src/security/plugin-permission-enforcer.ts` — capability-based enforcement
499-
- `packages/core/src/security/plugin-config-validator.ts` — config validation
500509
- `packages/runtime/src/sandbox/quickjs-runner.ts` — QuickJS-WASM sandbox (T1)
501510
- `packages/runtime/src/cloud/marketplace-install-local-plugin.ts` — local inline install (ADR-0016 §9)
502511
- `packages/runtime/src/cloud/marketplace-proxy-plugin.ts` — marketplace browse proxy

packages/core/ADVANCED_FEATURES.md

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -285,30 +285,6 @@ Plugins must use semantic versioning:
285285
'latest'
286286
```
287287

288-
### 10. Plugin Configuration Validation
289-
290-
Use Zod schemas to validate plugin configuration:
291-
292-
```typescript
293-
import { z } from 'zod';
294-
295-
const MyPluginConfigSchema = z.object({
296-
apiKey: z.string(),
297-
timeout: z.number().min(1000).max(30000),
298-
retries: z.number().int().min(0).default(3)
299-
});
300-
301-
const plugin: PluginMetadata = {
302-
name: 'my-plugin',
303-
version: '1.0.0',
304-
configSchema: MyPluginConfigSchema,
305-
306-
async init(ctx) {
307-
// Config is validated before init is called
308-
}
309-
};
310-
```
311-
312288
## Migration from LiteKernel
313289

314290
To migrate from `LiteKernel` to `ObjectKernel`:
@@ -367,11 +343,9 @@ Both kernels adhere to the same `Plugin` interface, but `ObjectKernel` supports
367343

368344
Extended `Plugin` interface with:
369345
- `version: string` - Semantic version
370-
- `configSchema?: z.ZodSchema` - Configuration schema
371346
- `signature?: string` - Plugin signature for verification
372347
- `healthCheck?(): Promise<PluginHealthStatus>` - Health check function
373348
- `startupTimeout?: number` - Startup timeout in milliseconds
374-
- `hotReloadable?: boolean` - Whether plugin supports hot reload
375349

376350
## Examples
377351

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
//
3+
// RUNTIME pins for the ADR-0049 retirements on `PluginMetadata` (#11982,
4+
// #12587), recorded in ADR-0025 §3.7: the security barrel must not publish the
5+
// retired validator again. These fail in `pnpm --filter @objectstack/core test`
6+
// the moment the export returns.
7+
//
8+
// The COMPILE-TIME half — a declared `configSchema` / `hotReloadable` no
9+
// longer type-checks against the published `PluginMetadata` — lives in
10+
// `packages/rest/src/plugin-metadata-retired-fields.pin.test.ts`, deliberately
11+
// NOT here: `@objectstack/core` has no `typecheck` script (type-check DEBT
12+
// ledger entry), so a `@ts-expect-error` in this package is a phantom pin no
13+
// tsc program a `typecheck` script runs would ever evaluate —
14+
// `check:type-check-coverage` refuses exactly that. The rest package's
15+
// `tsconfig.test.json` program is compiled by its `typecheck` script and reads
16+
// core's BUILT `.d.ts`, so the pin over there guards the published contract
17+
// itself.
18+
19+
import { describe, it, expect } from 'vitest';
20+
import * as securityBarrel from './security/index.js';
21+
22+
describe('PluginConfigValidator retirement (ADR-0049, ADR-0025 §3.7)', () => {
23+
it('no longer publishes PluginConfigValidator from the security barrel (#11982)', () => {
24+
expect((securityBarrel as Record<string, unknown>).PluginConfigValidator).toBeUndefined();
25+
expect((securityBarrel as Record<string, unknown>).createPluginConfigValidator).toBeUndefined();
26+
expect(Object.keys(securityBarrel)).not.toContain('PluginConfigValidator');
27+
expect(Object.keys(securityBarrel)).not.toContain('createPluginConfigValidator');
28+
});
29+
30+
it('positive control: the barrel still publishes its live siblings', () => {
31+
// Proves the absence assertions above read a populated namespace, not
32+
// an accidentally-empty import.
33+
expect(Object.keys(securityBarrel)).toContain('PluginSignatureVerifier');
34+
expect(Object.keys(securityBarrel)).toContain('PluginPermissionEnforcer');
35+
});
36+
});

packages/core/src/plugin-loader.ts

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import { Plugin, PluginContext } from './types.js';
44
import type { Logger } from '@objectstack/spec/contracts';
5-
import { z } from 'zod';
6-
import { PluginConfigValidator } from './security/plugin-config-validator.js';
75
import { parseSignature } from './security/plugin-artifact-signature.js';
86

97
/**
@@ -41,10 +39,15 @@ export interface ServiceRegistration {
4139
export interface PluginMetadata extends Plugin {
4240
/** Semantic version (e.g., "1.0.0") */
4341
version: string;
44-
45-
/** Configuration schema for validation */
46-
configSchema?: z.ZodSchema;
47-
42+
43+
// `configSchema` was retired on 2026-08-27 (ADR-0049 enforce-or-remove;
44+
// recorded in ADR-0025 §3.7): the loader's only call passed no config and
45+
// no caller could — plugin factories close over their config, so the
46+
// kernel never receives it. Plugins parse their own config at their own
47+
// seam instead (the `packages/rest` pattern). Re-declaring a kernel-owned
48+
// config-validation surface is a fresh decision for the day the ADR-0025
49+
// distribution layer lands.
50+
4851
/** Plugin signature for security verification */
4952
signature?: string;
5053

@@ -53,9 +56,12 @@ export interface PluginMetadata extends Plugin {
5356

5457
/** Startup timeout in milliseconds (default: 30000) */
5558
startupTimeout?: number;
56-
57-
/** Whether plugin supports hot reload */
58-
hotReloadable?: boolean;
59+
60+
// `hotReloadable` was retired on 2026-08-27 (#12587, same ADR-0049 batch):
61+
// declared and documented with zero reads — `HotReloadManager.reloadPlugin`
62+
// gates only on its own registered reload configs, so `hotReloadable:
63+
// false` was hot-reloaded identically to `true`. Reload participation is
64+
// governed solely by `HotReloadManager.registerReloadConfig`.
5965
}
6066

6167
/**
@@ -106,7 +112,6 @@ export interface VersionCompatibility {
106112
export class PluginLoader {
107113
private logger: Logger;
108114
private context?: PluginContext;
109-
private configValidator: PluginConfigValidator;
110115
private loadedPlugins: Map<string, PluginMetadata> = new Map();
111116
private serviceFactories: Map<string, ServiceRegistration> = new Map();
112117
private serviceInstances: Map<string, any> = new Map();
@@ -115,7 +120,6 @@ export class PluginLoader {
115120

116121
constructor(logger: Logger) {
117122
this.logger = logger;
118-
this.configValidator = new PluginConfigValidator(logger);
119123
}
120124

121125
/**
@@ -153,11 +157,6 @@ export class PluginLoader {
153157
throw new Error(`Version incompatible: ${versionCheck.message}`);
154158
}
155159

156-
// Validate configuration if schema is provided
157-
if (metadata.configSchema) {
158-
this.validatePluginConfig(metadata);
159-
}
160-
161160
// Verify signature if provided
162161
if (metadata.signature) {
163162
await this.verifyPluginSignature(metadata);
@@ -403,22 +402,6 @@ export class PluginLoader {
403402
return semverRegex.test(version);
404403
}
405404

406-
private validatePluginConfig(plugin: PluginMetadata, config?: any): void {
407-
if (!plugin.configSchema) {
408-
return;
409-
}
410-
411-
if (config === undefined) {
412-
// In loadPlugin, we often don't have the config yet.
413-
// We skip validation here or valid against empty object if schema allows?
414-
// For now, let's keep the logging behavior but note it's delegating
415-
this.logger.debug(`Plugin ${plugin.name} has configuration schema (config validation postponed)`);
416-
return;
417-
}
418-
419-
this.configValidator.validatePluginConfig(plugin, config);
420-
}
421-
422405
private async verifyPluginSignature(plugin: PluginMetadata): Promise<void> {
423406
if (!plugin.signature) {
424407
return;

packages/core/src/security/index.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*
66
* Provides security features for the ObjectStack microkernel:
77
* - Plugin signature verification
8-
* - Plugin configuration validation
98
* - Permission and capability enforcement
109
*
1110
* @module @objectstack/core/security
@@ -35,10 +34,11 @@ export {
3534
verifyPluginArtifact,
3635
} from './plugin-artifact-signature.js';
3736

38-
export {
39-
PluginConfigValidator,
40-
createPluginConfigValidator,
41-
} from './plugin-config-validator.js';
37+
// `PluginConfigValidator` / `createPluginConfigValidator` were RETIRED here on
38+
// 2026-08-27 (#11982, ADR-0049 enforce-or-remove; recorded in ADR-0025 §3.7).
39+
// The kernel never received a plugin's config to validate — factories close
40+
// over it — so the class had zero live callers; plugins parse their own
41+
// config at their own seam instead.
4242

4343
export {
4444
PluginPermissionEnforcer,

0 commit comments

Comments
 (0)