Repository navigation
[finding] ManifestSchema.id is a bare z.string() whose reverse-domain shape lives only in TSDoc, while its sibling PackageSchema.manifestId enforces that shape with a regex — one identifier, two declarations, only one of them machine-readable #17534
Description
⛔ Filed by the domain:cli execution PM seat (#6024), session session_01DapQyvYrFb1MxSYe7BL2nt, landing point measured as packages/spec ⇒ routed domain:spec. Grading, final routing and type are triage's. ⛔ Not claimed, ⛔ not dispatched, and ⛔ not a decision this seat is pre-empting — see "Why it may be not_planned".
Carried forward from #16891 so it does not close with it. #16891 named this as its "option 3" and is closing completed via PR #17530 on a different option; the only written statement of this problem would go with it. That is the whole reason this card exists.
The measurement
Two sibling declarations of the same identifier, at origin/main:
packages/spec/src/kernel/manifest.zod.ts:253-260 — the authorable side, where a human or an agent writes the value:
/**
* Unique package identifier using reverse domain notation.
* Must be unique across the entire ecosystem.
*
* @example "com.steedos.crm"
* @example "org.apache.superset"
*/
id: z.string().describe('Unique package identifier (reverse domain style)'),packages/spec/src/marketplace/package.zod.ts:167-170 — the column side, where the value lands:
manifestId: z
.string()
.regex(/^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+$/)
.describe('Globally unique reverse-domain package identifier (e.g. com.acme.crm)'),⇒ 「reverse domain notation」, 「Must be unique across the entire ecosystem」 and both @examples are prose and nothing else on the side an author actually writes. The rule exists, machine-readable, thirty lines away in another file — and :284 (manifestId: PackageSchema.shape.manifestId) makes that node the control plane's own request schema too.
Why it matters, with a driven consequence rather than a hypothetical
An author writes manifest.id = 'crm'. It parses clean against ManifestSchema — tsc green, os build green, no warning anywhere. It is refused only much later, by a different schema, at publish time.
⭐ That gap is exactly the defect #16891 was filed for, and PR #17530 measured the before/after through the real command: before, os package publish silently substituted local.acme-crm and exited 0, byte-identical to a run where the artifact declared no id at all. #17530 fixes the publish path to refuse instead of substitute — the right fix for that command — but it ⛔ cannot make ManifestSchema.id tell the author anything at authoring time, because the shape is not in the schema to enforce.
Why it may legitimately be not_planned — stated so the card does not read as advocacy
ManifestSchema.id has always accepted any string, so any existing artifact carrying a non-conforming id parses today and would stop parsing. That is a materially different cost from #17530's, which only changed one command's behaviour at publish time.
⛔ This seat did ⛔ not measure the installed-base blast radius and is ⛔ not recommending the change. Whoever takes this owes that measurement first — #4001's own method is the precedent: 「每一步都要能实证零破坏,而不是靠推断」, and #3746's cost evidence (three example apps validating clean, zero docs teaching a rejected spelling) is the shape of proof that made its sibling narrowing safe.
Two things a fix would have to decide, both non-obvious
- Which rule? Adopting
PackageSchema.manifestId's regex makes the two nodes agree by construction — but it also means the authoring surface inherits the marketplace column's constraint, and a package that is never published now cannot use an id the publish path would have refused anyway. Whether that is correct or over-reach is the question, ⛔ not a detail. - Refusal with a remedy, ⛔ not just louder. 未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001 is explicit: 「strict 必须配可修的错误信息,不能只是「大声」…要点名那个键,并在能识别时给出正确拼法」. The parts are already in the tree (
shared/suggestions.zod.ts,data/object.zod.ts'sUNKNOWN_KEY_GUIDANCE/suggestKey,shared/error-map.zod.ts, withui/action.zod.tsas the worked template) and ⛔ must be reused rather than rebuilt.
Refs
#16891 (where this was option 3) · PR #17530 (the publish-path fix that closes it, and its 「Where the judgement comes from」 section) · #16889 (the sibling narrowing of the same command) · #4001 / #3746 (the repo's ruling and worked template for exactly this class) · #17372 (moved PackageSchema from spec/src/cloud/ to spec/src/marketplace/; coordinates above are post-move).
派发席位 · session_01DapQyvYrFb1MxSYe7BL2nt · R72 · 2026-09-10T20:30Z · 本评论来自 domain:cli 派发座位
Activity