Filed by the domain:spec execution seat while implementing #16365 (widen PluginSchema.version onto the loader's grammar), using Claude Code. ⛔ Deliberately not repaired there: #16365's ruling forbids narrowing this key, and every repair here is a narrowing. No severity asserted, no lane asserted — both are triage's.
The finding
Two declarations call themselves a semantic version and accept eight strings SemVer 2.0.0 forbids.
| Declaration |
Spelling |
PluginSchema.version — packages/spec/src/kernel/plugin.zod.ts, described "Semantic Version" |
/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/ |
PluginLoader.isValidSemanticVersion — packages/core/src/plugin-loader.ts |
the same regex, character for character |
Measured against the official SemVer 2.0.0 regex over the corpus of examples the SemVer spec text itself lists, plus this repo's own pinned strings. The grammar accepts every SemVer-valid string — there is no gap in that direction — and additionally accepts these, which SemVer 2.0.0 forbids:
- Leading zeroes in the numeric core (SemVer 2.0.0 §2: numeric identifiers "MUST NOT include leading zeroes"):
01.1.1, 1.01.1, 1.1.01
- Leading zeroes / empty identifiers in the prerelease (§9):
1.0.0-0123, 1.0.0-alpha..1, 1.0.0-alpha.., 1.0.0-.
- Degenerate build metadata (§10, identifiers must be non-empty):
1.0.0+.
Reproduce, no build needed:
node -e "const g=/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?\$/; console.log(['01.1.1','1.01.1','1.1.01','1.0.0-0123','1.0.0-alpha..1','1.0.0+.'].map(v=>v+' '+g.test(v)).join('\n'))"
Why it is being filed rather than resolved in place
⭐ The leading-zero half is older than #16365 and was never introduced by it. PluginSchema.version's pre-#16365 regex, /^\d+\.\d+\.\d+$/, already accepted 01.1.1, 1.01.1 and 1.1.01 — \d+ has always admitted them. #16365 widened the key by adding the two optional suffix groups and changed nothing about the numeric core.
That is exactly why it could not be fixed under #16365. That card's ruling was widen the spec, do not narrow, on the ground that "nothing that loads today stops loading". Tightening the numeric core to the official SemVer regex would refuse 01.1.1, which PluginSchema accepts today and accepted before — a narrowing, and a published-behaviour change on both the schema and the loader, wanting its own statement.
The shape of the decision
- Tighten both spellings to the official SemVer 2.0.0 regex. The two stay converged, and
describe('Semantic Version') becomes exactly true. ⚠️ Refuses plugin objects that load today. No in-repo fixture or pin uses any of the eight forms (checked), so the blast radius is external plugins only — but that is precisely the population that cannot be measured from here.
- Keep the grammar and qualify the prose, the way
ManifestSchema.version already does: its TSDoc says (major.minor.patch) explicitly and manifest.test.ts pins 1.0.0-beta invalid, so its narrow regex and its prose agree. The parallel move here is to stop calling this key plain "Semantic Version" and say what it actually accepts.
- Do nothing, on the ground that a lenient validator upstream of a real refusal costs nobody anything.
⛔ This seat does not grade which. The asymmetry that decided #16365 does not repeat here: there the wide answer was free, and here every answer that makes the declaration true has a cost.
Adjacent
Generated by Claude Code
Filed by the
domain:specexecution seat while implementing #16365 (widenPluginSchema.versiononto the loader's grammar), using Claude Code. ⛔ Deliberately not repaired there: #16365's ruling forbids narrowing this key, and every repair here is a narrowing. No severity asserted, no lane asserted — both are triage's.The finding
Two declarations call themselves a semantic version and accept eight strings SemVer 2.0.0 forbids.
PluginSchema.version—packages/spec/src/kernel/plugin.zod.ts, described"Semantic Version"/^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/PluginLoader.isValidSemanticVersion—packages/core/src/plugin-loader.tsMeasured against the official SemVer 2.0.0 regex over the corpus of examples the SemVer spec text itself lists, plus this repo's own pinned strings. The grammar accepts every SemVer-valid string — there is no gap in that direction — and additionally accepts these, which SemVer 2.0.0 forbids:
01.1.1,1.01.1,1.1.011.0.0-0123,1.0.0-alpha..1,1.0.0-alpha..,1.0.0-.1.0.0+.Reproduce, no build needed:
Why it is being filed rather than resolved in place
⭐ The leading-zero half is older than #16365 and was never introduced by it.
PluginSchema.version's pre-#16365 regex,/^\d+\.\d+\.\d+$/, already accepted01.1.1,1.01.1and1.1.01—\d+has always admitted them. #16365 widened the key by adding the two optional suffix groups and changed nothing about the numeric core.That is exactly why it could not be fixed under #16365. That card's ruling was widen the spec, do not narrow, on the ground that "nothing that loads today stops loading". Tightening the numeric core to the official SemVer regex would refuse
01.1.1, whichPluginSchemaaccepts today and accepted before — a narrowing, and a published-behaviour change on both the schema and the loader, wanting its own statement.The shape of the decision
describe('Semantic Version')becomes exactly true.ManifestSchema.versionalready does: its TSDoc says(major.minor.patch)explicitly andmanifest.test.tspins1.0.0-betainvalid, so its narrow regex and its prose agree. The parallel move here is to stop calling this key plain "Semantic Version" and say what it actually accepts.⛔ This seat does not grade which. The asymmetry that decided #16365 does not repeat here: there the wide answer was free, and here every answer that makes the declaration true has a cost.
Adjacent
PluginSchema.versionrefuses the prerelease and build-metadata forms SemVer defines, while the loader that actually runs accepts them #16365 — where this was measured; its PR carries the measurement, a comment on the key and a test pinning the fringe so it is stated rather than silent.Generated by Claude Code