You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found while exempting onEnable from the new top-level authoring-key lint (#4167 / #4189). Checking which lifecycle hooks the runtime actually honours turned up that only one of the five does.
The finding
PluginLifecycleSchema (packages/spec/src/kernel/plugin.zod.ts:80-90) declares five hooks, each with descriptive TSDoc, and PluginLifecycleHooks (packages/spec/src/system/types/plugin.ts) mirrors them:
hook
declared
invoked anywhere
onInstall
✓
0 call sites
onEnable
✓
3 (packages/runtime/src/app-plugin.ts)
onDisable
✓
0 call sites
onUninstall
✓
0 call sites
onUpgrade
✓
0 call sites
Method: repo-wide grep over packages/, apps/, examples/ excluding dist/ and tests, for both direct (.onInstall() and indexed ([hook]() invocation, plus a check for any dynamic lifecycle dispatch table. Nothing. onEnable is the only hook with a runtime that runs it.
onUpgrade has an entire supporting type built for it — UpgradeContextSchema with previousVersion / newVersion / isMajorUpgrade (plugin.zod.ts:59-76), exported from the package root — for a callback nothing ever calls.
The part that actively misleads
packages/spec/src/kernel/metadata-type-schemas.ts documents onInstall as the place to call two registration functions:
:134 * Plugins that introduce custom metadata types — declared through
* `additionalTypes` on `MetadataPluginConfig` — should call this from
* their `onInstall` hook so the engine's `/meta/types/:type` endpoint
* starts emitting a real JSON Schema for them.
:167 * Plugins call this from `onInstall` to layer actions onto any type —
* built-in or custom — without forking the registry.
A plugin author who follows that instruction exactly registers nothing. registerMetadataTypeSchema / registerMetadataTypeActions never run, /api/v1/meta/types/:type keeps emitting no schema and no type-level buttons, and there is no error — the hook simply never fires. That is the same silent-drop shape as #3786/#4148/#4167, one layer up: the guidance is the thing that's dead, not just the property.
Why it matters
declared ≠ enforced (Prime Directive chore: version packages #10) on the plugin system's own contract. packages/spec is the constitution; five hooks are written into it and four have no implementation.
It is load-bearing advice, not just an unused field. The metadata-type registry is the documented extension point for custom metadata types, and its documented entry point does not exist.
It propagates: packages/cli/src/utils/config.ts:93 and packages/cli/src/commands/serve.ts:676 both name onDisable alongside onEnable when preserving module-level named exports, so the CLI carefully carries a hook to a runtime that will not call it.
Suggested resolution — the spec-property-retirement playbook
Per ADR-0049 enforce-or-remove, each of the four needs a decision, and they may not all get the same one:
onInstall — highest priority, because it has documented consumers. Either implement it (the metadata-type registry needs some install-time seam) or rewrite both TSDoc blocks to name a hook that runs. Rewriting the docs to say onEnable is probably the cheap correct answer, but only if onEnable's timing is actually right for registry population — worth checking rather than assuming.
onDisable / onUninstall — no consumers found, no callers. Retire, or implement as part of a real enable/disable lifecycle if one is planned.
onUpgrade — retire together with UpgradeContextSchema, or wire it into the package-upgrade path that already exists (packages/spec/src/kernel/package-upgrade.ts), which looks like where it was meant to go.
Whichever way each goes, the lint family this came out of would like a note: onEnable is now in STACK_RUNTIME_MEMBERS precisely because the runtime honours it, and onDisable is deliberately excluded because it does not. If onDisable ever becomes real, that exclusion has to move with it — a test in packages/spec/src/kernel/metadata-authoring-lint.test.ts pins the current distinction and will fail loudly, which is the intended alarm.
I have not implemented anything here; #4189 only documents the current state where it had to.
Found while exempting
onEnablefrom the new top-level authoring-key lint (#4167 / #4189). Checking which lifecycle hooks the runtime actually honours turned up that only one of the five does.The finding
PluginLifecycleSchema(packages/spec/src/kernel/plugin.zod.ts:80-90) declares five hooks, each with descriptive TSDoc, andPluginLifecycleHooks(packages/spec/src/system/types/plugin.ts) mirrors them:onInstallonEnablepackages/runtime/src/app-plugin.ts)onDisableonUninstallonUpgradeMethod: repo-wide grep over
packages/,apps/,examples/excludingdist/and tests, for both direct (.onInstall() and indexed ([hook]() invocation, plus a check for any dynamic lifecycle dispatch table. Nothing.onEnableis the only hook with a runtime that runs it.onUpgradehas an entire supporting type built for it —UpgradeContextSchemawithpreviousVersion/newVersion/isMajorUpgrade(plugin.zod.ts:59-76), exported from the package root — for a callback nothing ever calls.The part that actively misleads
packages/spec/src/kernel/metadata-type-schemas.tsdocumentsonInstallas the place to call two registration functions:A plugin author who follows that instruction exactly registers nothing.
registerMetadataTypeSchema/registerMetadataTypeActionsnever run,/api/v1/meta/types/:typekeeps emitting no schema and no type-level buttons, and there is no error — the hook simply never fires. That is the same silent-drop shape as #3786/#4148/#4167, one layer up: the guidance is the thing that's dead, not just the property.Why it matters
declared ≠ enforced(Prime Directive chore: version packages #10) on the plugin system's own contract.packages/specis the constitution; five hooks are written into it and four have no implementation.packages/cli/src/utils/config.ts:93andpackages/cli/src/commands/serve.ts:676both nameonDisablealongsideonEnablewhen preserving module-level named exports, so the CLI carefully carries a hook to a runtime that will not call it.Suggested resolution — the
spec-property-retirementplaybookPer ADR-0049 enforce-or-remove, each of the four needs a decision, and they may not all get the same one:
onInstall— highest priority, because it has documented consumers. Either implement it (the metadata-type registry needs some install-time seam) or rewrite both TSDoc blocks to name a hook that runs. Rewriting the docs to sayonEnableis probably the cheap correct answer, but only ifonEnable's timing is actually right for registry population — worth checking rather than assuming.onDisable/onUninstall— no consumers found, no callers. Retire, or implement as part of a real enable/disable lifecycle if one is planned.onUpgrade— retire together withUpgradeContextSchema, or wire it into the package-upgrade path that already exists (packages/spec/src/kernel/package-upgrade.ts), which looks like where it was meant to go.Whichever way each goes, the lint family this came out of would like a note:
onEnableis now inSTACK_RUNTIME_MEMBERSprecisely because the runtime honours it, andonDisableis deliberately excluded because it does not. IfonDisableever becomes real, that exclusion has to move with it — a test inpackages/spec/src/kernel/metadata-authoring-lint.test.tspins the current distinction and will fail loudly, which is the intended alarm.I have not implemented anything here; #4189 only documents the current state where it had to.