Skip to content

[finding] @objectstack/spec has four grammars for a package version, and ManifestSchema refuses the 2.0.0-beta.1 that PackageVersionSchema's own docstring advertises #18697

Description

@hotlong

Blocked-by: #17534RELEASED 2026-09-21T00:18Z. ⭐ The line named the card; the condition the hold actually rested on was the PR. The ruling's Execution clause reads 「Serial behind PR #18319 (holds packages/spec/src/kernel/manifest.zod.ts)」, and PR #18319 merged 2026-09-21T00:08:02Z as 097d2685 on origin/main (read twice). Card #17534 is still open and still pm:dispatched — that is a half-state on its card, ⛔ not a live block on this one. ⛔ Leaving the line pointing at an open card would recreate the exact body-vs-state contradiction this card was parked to fix.

@objectstack/spec carries four different grammars for "the version of a package", and the
strictest of them refuses a string that its own sibling advertises as an example.

All four measured on bdea10a185d422ef1f9022e210b87d19882055ee (the commit objectstack-ai/cloud
pins today) via the contents API, 2026-09-17T15:29Z:

carrier regex prerelease case
ManifestSchema.versionpackages/spec/src/kernel/manifest.zod.ts:322 ^\d+\.\d+\.\d+$ none at all
PluginSchema.versionpackages/spec/src/kernel/plugin.zod.ts:212 ^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ yes upper and lower
PackageVersionSchema.versionpackages/spec/src/marketplace/package-version.zod.ts:144-146 ^\d+\.\d+\.\d+(-[a-z0-9.-]+)?(\+[a-z0-9.-]+)?$ yes lower only
PackageManifestSchema.version — same file, :80 no regex — bare z.string() unconstrained unconstrained

The declared-contract violation, quoted

PackageVersionSchema.version's own JSDoc, verbatim at that commit:

/** Semantic version string (e.g. 1.2.3, 2.0.0-beta.1). */

ManifestSchema.version's regex is ^\d+\.\d+\.\d+$ ⇒ it refuses 2.0.0-beta.1, the very
string the sibling schema documents as an example of the same concept. That is class (b): a declared
contract contradicted by an enforced one, with the contract text quoted above.

⚠️ PackageManifestSchema.version at :80 is the one nobody has named yet — it is a bare
z.string(), so it constrains nothing at all. A downstream that trusts "spec validated it" gets
no validation from this carrier.

Honest limit on severity — ⛔ do not grade this from the table alone

What is measured here is the schema disagreement. What is NOT measured is whether any live path
judges a real bundle's manifest.version with ManifestSchema — if none does, this is a latent
trap rather than a today-defect, and the grading should say so. Whoever triages this should answer
that question first; it decides the class as much as the table does.

Where this came from

Found while implementing objectstack-ai/cloud#2305 (PR objectstack-ai/cloud#2320), which had to
pick one of these grammars for the cloud package-publish route. It picked
PackageVersionSchema.version deliberately, because that accept set is a strict SUBSET of
PluginSchema.version — the one the runtime plugin loader parses with — so nothing that gets past
the publish gate can fail the runtime parse. That choice is sound whatever happens to this card;
⛔ this card is not a blocker for it.

One consequence worth stating for whoever fixes this: cloud's publish route now refuses a
semver-valid prerelease with an uppercase identifier (1.0.0-Beta.1) that the runtime loader
would have accepted. That is a deliberate, safe-direction narrowing today; if this card converges
the grammars, that fringe disappears with it.

查重词

semver grammar divergence spec · ManifestSchema.version regex · PackageVersionSchema.version ·
PluginSchema version prerelease case · package version schema four spellings


os-decision-facets

  • ① 项目长远合理性:A 让被强制的语法等于仓里已公开宣称的那句话,缩小特例与契约增生(10 载体 → 3 声明);B/C 把假声明搬到另一个载体上,增生不减。
  • ② 实际业务拉动:有实测 —— git tag 483/7611 与 packages/spec/CHANGELOG.md 版本标题 8/170 是预发布,而描述包的那个键表达不出预发布;反向零拉动 —— 退化形态(01.1.1 / 1.0.0-0123 / 1.0.0-alpha..1 / 1.0.0+.)在 examples/**packages/apps/**零生产者(亮控:同模式全树 43)。
  • ③ 防 AI 犯错:A 是唯一让「AI 的直觉」与「被强制的语法」一致的选项;B 永不拒绝作者,代价是把 1.0.0-alpha..1 这类无法排序的串永久册封进已发布面 —— 静默容忍胜过响亮拒绝的反面。
  • ④ 创业阶段不扩散:第一阶段(10 → 3 个被引用声明)即本轴一步;本轮新发现的三个「已发布但无人解析」载体证明,语法写成字面量时载体数会自己长。

Prior rulings read: version grammar,semver,manifest.version,prerelease → 1 hits; ADR-0087 D1

⚠️ 那条 ADR 读过了,它不答本卡:ADR-0087 D1 是协议握手(PROTOCOL_VERSIONengines.protocol 的检查与结构化拒绝),命中只是 :106 上的 semver 一词;它⛔ 没有裁定包清单 version在十个载体上的文法。⇒ 本卡仍是决策卡,⛔ 不是执行卡。(判据出自 check-prior-rulings.mjs 的警告「Read the decision before presenting the card」—— 本席读了。)

推荐:A(SemVer 2.0.0)。自检:只看①选 A;②③④ 是否翻转:② 不翻(两侧都支持 A:有预发布拉动、退化形态零拉动)· ③ 不翻(A 是唯一直觉=强制的选项)· ④ 不翻(第一阶段本身就是不扩散)。⇒ 四棱同向 A。

置信缺口:objectstack-ai/cloud 未挂载 ⇒ 它的 publish 路由接受集在 A/B/C 下都会动而本轮无法读;且四套文法的判读取自正则字面量而非运行期 Zod 判决(刻意不建构,避开陈旧 dist)。


Generated by Claude Code

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

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions