Skip to content

[finding] ManifestSchema.version's own TSDoc @example "2.1.0-beta.1" is refused by its regex — copy the documented example and parse throws #17461

Description

@os-bill

Filed by the domain:spec execution seat while implementing #17070, using Claude Code. ⛔ Deliberately not repaired there: #17070's declared file face is PluginSchema.version and PluginLoader's predicate, and this key is neither. No severity asserted, no lane asserted — both are triage's.

The finding

ManifestSchema.version (packages/spec/src/kernel/manifest.zod.ts) documents two @example values in its TSDoc. Its regex accepts one of them and refuses the other.

  /**
   * Package version following semantic versioning (major.minor.patch).
   *
   * @example "1.0.0"
   * @example "2.1.0-beta.1"      <-- refused by the regex two lines below
   */
  version: z.string().regex(/^\d+\.\d+\.\d+$/).describe('Package version (semantic versioning)'),

Reproduce, no build needed:

node -e "const re=/^\d+\.\d+\.\d+$/; for (const v of ['1.0.0','2.1.0-beta.1']) console.log(re.test(v), JSON.stringify(v))"

true "1.0.0"
false "2.1.0-beta.1"

An author who copies the second documented example verbatim gets a ZodError from ManifestSchema.parse. This is a documented example that fails when copied, not a missing member or a stale prose nuance.

It is not an accident of the regex — the refusal is pinned

manifest.test.ts already pins the same class as invalid, deliberately:

const invalidVersions = ['1.0', '1', 'v1.0.0', '1.0.0-beta'];

So the schema, its test, and its prose sentence (major.minor.patch) all agree that prerelease suffixes are refused. The only artifact that disagrees is the @example line — which is why this reads as an editing residue in the TSDoc rather than a question about what the key should accept.

Why it is being filed rather than resolved in place

#17070 rules on PluginSchema.version and PluginLoader, and its dispatch declared a file face that does not include manifest.zod.ts. This key is also the precedent #17070 was told to copy — its TSDoc spells (major.minor.patch) explicitly instead of leaning on the word SemVer — so touching it in the same change would have edited the yardstick and the thing being measured at once.

The shape of the decision

  • Delete or correct the @example. Cheapest, and consistent with the pinned refusal: replace "2.1.0-beta.1" with a value the regex accepts. Nothing published moves.
  • Widen the regex to admit prerelease/build, matching what PluginSchema.version now accepts. ⚠️ This changes an accept set on a published schema and contradicts the existing pin, so it wants its own statement — and note the two keys are deliberately different grammars today.
  • Do nothing, on the ground that one wrong example in a TSDoc costs little.

⛔ This seat does not grade which.

Adjacent

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions