You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
shape has "sharing": true
shape has "embed": true
shape has "mobileNavigation": true
form offers: [ 'mobileNavigation', 'sharing', 'embed' ]
AppSchema.safeParse with sharing: success=false :: `App.sharing` was removed in @objectstack/spec 17.0.0 …
AppSchema.safeParse with embed: success=false :: `App.embed` was removed in @objectstack/spec 17.0.0 …
control (no retired key): success= true
在 #5015(
EmbedConfigSchema/NotificationActionSchemaADR-0049 退役)的前置核验中顺带测到,与该单无因果关系,单独立案。事实
packages/spec/src/ui/app.form.ts的「Access & sharing」「…」分区仍然给作者渲染三个 composite 输入:而这三个键在
packages/spec/src/ui/app.zod.ts:1227/1233/1240早已是retiredKey()墓碑(17.0.0,2026-06 liveness audit / ADR-0049)。retiredKey()是z.never({...}).optional(),所以填进去不是「静默丢弃」,是硬解析失败。实测(
AppSchema=getMetadataTypeSchema('app'),同一次运行带阳性对照):阳性对照(不带退役键的同一份 payload)通过,所以
success=false是关于这三个键的事实,不是 payload 本身有问题。为什么门没开火 —— 这才是要记的那一半
packages/spec/src/system/metadata-form-zod-reconciliation.test.ts(#3786)有一条正好针对这个形状的断言:它用
keysOf(root)取 shape 的键来判定。retiredKey()按设计把键留在 walked shape 里(spec-property-retirement 套件里写明的那条:墓碑路线的 liveness 行必须保留,正因为键还在 shape 中)。于是墓碑键对这条断言永远是「Zod accepts」,门绿着,而作者拿到的是 422。即门的谓词写的是
key ∈ shape,要判的却是key 是真实可授权面。两者在retiredKey()出现之前是同一件事,之后不是了。顺带:该断言的措辞("silently stripped on save")现在也只覆盖了两种失败里的一种 —— 未声明键静默丢弃、墓碑键硬失败,后者更响但更早,作者在控制台里根本不该看到这个控件。
建议范围(PM 定,不在本单做)
app.form.ts摘掉这三个{ field: … }输入,按套件惯例在原位留一行注释说明退役出处;key ∈ shape收紧为key ∈ shape 且不是 retiredKey(z.never内层判定即可),这样下一个墓碑键落地时门自己会红 —— 否则同样的漂移会在每一次retiredKey()退役后重演;*.form.ts是否还有别的墓碑键在被提供(本单没有全量扫)。第 2 条是这单真正的价值:没有它,第 1 条只是把当前这三个擦掉,门依旧是瞎的。
现场
packages/spec/src/ui/app.form.ts(约 51-59 行)packages/spec/src/ui/app.zod.ts:1227/:1233/:1240packages/spec/src/system/metadata-form-zod-reconciliation.test.ts(every field the form offers is a key the Zod accepts)