Filed by the domain:engine execution seat while implementing #16049 (enforce PluginSchema at kernel.use()), using Claude Code. ⛔ Deliberately not repaired there: it is packages/spec work, and the ruling on #16049 was about type. No severity asserted, no lane asserted — both are triage's.
The finding
Two declarations in this repository disagree about what a plugin version is, and the disagreement became load-bearing the moment the boot path started running the schema.
| Declaration |
Grammar |
Accepts 1.0.0-alpha.1 / 1.0.0+20230101 |
PluginSchema.version — packages/spec/src/kernel/plugin.zod.ts, described as "Semantic Version" |
/^\d+\.\d+\.\d+$/ |
no |
PluginLoader.isValidSemanticVersion — packages/core/src/plugin-loader.ts, the check the boot path has always run |
/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/ |
yes |
SemVer 2.0.0 defines both prerelease and build metadata as part of a semantic version, so the spec's spelling is the narrow one and the loader's is the correct one. The spec key's own describe() says "Semantic Version" without qualification.
Why it is being filed rather than resolved in place
The loader's acceptance is pinned, deliberately, in two named cases — packages/core/src/plugin-loader.test.ts:
should accept versions with pre-release tags (1.0.0-alpha.1)
should accept versions with build metadata (1.0.0+20230101)
and two in-repo class-based plugin fixtures ship version = '0.0.0-fixture' through a real kernel boot (packages/cli/test/serve-organizations-host-resolution.e2e.test.ts, packages/verify/src/harness.host-resolution.test.ts).
So enforcing PluginSchema.version verbatim at kernel.use() would not have enforced the protocol — it would have retired a pinned capability, silently, under a card whose ruling enumerated three refusal classes (unknown type, invalid slug, invalid homepage) and did not name version. PR #16363 therefore excludes the version key from the enforced projection, declares the exclusion in the code, and pins it with tests. That exclusion is a stopgap, and this card is the place the disagreement gets settled.
The shape of the decision
- widen the spec — give
PluginSchema.version the full SemVer 2.0.0 grammar, matching the loader and the key's own describe. The two spellings converge, the exclusion in PluginLoader.validatePluginContract can be dropped, and nothing that loads today stops loading. Costs a packages/spec change with its generated baselines (gen:schema, check:authorable-surface) and a changeset — the published JSON Schema for this key widens.
- narrow the runtime — keep the spec regex, drop the exclusion, and retire the two loader pins. ⚠️ This refuses plugin objects that load today, including any plugin published on a prerelease version, and would want stating as a published-behaviour change in its own right.
⛔ This seat does not grade which; the first looks strictly better to it, but "the protocol is the baseline" is the standing principle and the two answers are not symmetric in what they cost consumers.
Adjacent
Generated by Claude Code
Filed by the
domain:engineexecution seat while implementing #16049 (enforcePluginSchemaatkernel.use()), using Claude Code. ⛔ Deliberately not repaired there: it ispackages/specwork, and the ruling on #16049 was abouttype. No severity asserted, no lane asserted — both are triage's.The finding
Two declarations in this repository disagree about what a plugin
versionis, and the disagreement became load-bearing the moment the boot path started running the schema.1.0.0-alpha.1/1.0.0+20230101PluginSchema.version—packages/spec/src/kernel/plugin.zod.ts, described as "Semantic Version"/^\d+\.\d+\.\d+$/PluginLoader.isValidSemanticVersion—packages/core/src/plugin-loader.ts, the check the boot path has always run/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/SemVer 2.0.0 defines both prerelease and build metadata as part of a semantic version, so the spec's spelling is the narrow one and the loader's is the correct one. The spec key's own
describe()says "Semantic Version" without qualification.Why it is being filed rather than resolved in place
The loader's acceptance is pinned, deliberately, in two named cases —
packages/core/src/plugin-loader.test.ts:should accept versions with pre-release tags(1.0.0-alpha.1)should accept versions with build metadata(1.0.0+20230101)and two in-repo class-based plugin fixtures ship
version = '0.0.0-fixture'through a real kernel boot (packages/cli/test/serve-organizations-host-resolution.e2e.test.ts,packages/verify/src/harness.host-resolution.test.ts).So enforcing
PluginSchema.versionverbatim atkernel.use()would not have enforced the protocol — it would have retired a pinned capability, silently, under a card whose ruling enumerated three refusal classes (unknowntype, invalidslug, invalidhomepage) and did not nameversion. PR #16363 therefore excludes theversionkey from the enforced projection, declares the exclusion in the code, and pins it with tests. That exclusion is a stopgap, and this card is the place the disagreement gets settled.The shape of the decision
PluginSchema.versionthe full SemVer 2.0.0 grammar, matching the loader and the key's own describe. The two spellings converge, the exclusion inPluginLoader.validatePluginContractcan be dropped, and nothing that loads today stops loading. Costs apackages/specchange with its generated baselines (gen:schema,check:authorable-surface) and a changeset — the published JSON Schema for this key widens.⛔ This seat does not grade which; the first looks strictly better to it, but "the protocol is the baseline" is the standing principle and the two answers are not symmetric in what they cost consumers.
Adjacent
PluginSchemamakesstaticPath/slugreally required fortype: "ui"(superRefine), and core'sPlugininterface derives fromPluginDefinition(spec half of #16049) #16334 — the otherpackages/specPluginSchemachange already queued (thetype: "ui"superRefine and thePlugininterface alignment). Same file, so whoever takes that one should read this before touchingplugin.zod.ts.Generated by Claude Code