在 #5280(app.form.ts 提供墓碑键)的全量扫尾中顺带发现,与该单同类不同面 —— 那单的范围是 packages/spec/src/**/*.form.ts 与 #3786 对账门,手写文档面不在其内,故单独立案。
事实
content/docs/ui/apps.mdx 有三处仍把 17.0.0 已退役的键当作可作者化面在教:
| 位置 |
内容 |
实际状态 |
第 17 行(第 12 行 os:check 块内) |
version: '1.0.0' |
App.version 是 retiredKey() 墓碑 |
| 第 45 行(App Properties 表) |
一行 version / string / optional / App version |
同上,表格把它列为可选可写 |
| 第 215-224 行 |
整节 ## Mobile Navigation,含 mobileNavigation: { mode, bottomNavItems } 示例与 mode 取值说明 |
App.mobileNavigation 是 retiredKey() 墓碑(完全未实现) |
第 235 行(第 230 行 os:check 块内) |
version: '2.0.0' |
同 version |
墓碑是 z.never().optional(),所以照抄不是「多写个没用的键」,是整条 save 硬失败。实测(worktree @ origin/main de113a4,把第 12 行 os:check 块的对象原样喂给 getMetadataTypeSchema('app')):
apps.mdx "Basic Structure" example parses: false
version :: `App.version` was removed in @objectstack/spec 17.0.0 (2026-06 liveness audit — no consumer in framework or ob…
为什么门没开火
两个示例块都带 {/* os:check */},check:skill-examples 也确实在跑(本机 ✅ 204 prose examples type-check against @objectstack/spec)。它绿是因为这个门只做 tsc,而两个块都是无类型标注的对象字面量(const crmApp = { … } / const projectApp = { … }):没有任何东西把它们和 AppSchema 关联起来,retiredKey() 赖以在编译期开火的 never 入参类型于是永远不参与推断。
即:os:check 覆盖的是「示例能否编译」,不是「示例是否合规」。对退役键这一类,前者恒真。两条可能的收紧路径(择一,需裁决):
- A. 示例侧:给这类块加标注(
const crmApp: App = { … } 或 defineApp({ … })),让 never 入参在 tsc 就红。改动面是文档示例,门本身不动。
- B. 门侧:
check:skill-examples 对能推断出 schema 的块追加一次 safeParse。覆盖更广(标注漏了也能抓),但要先解决「哪个块对应哪个 schema」的归属问题。
我倾向 A + 逐步 B:A 立刻可做且零门改动;B 是长期正确的那一半,但归属推断本身需要设计。
顺带
同类不同页已有 #5291(widget-contract 的 Theme 段在教 #3494 删掉的 density)。两单的共同形状是「手写文档面没有随退役一起清」,若要一次性收口,值得考虑一条覆盖 content/docs/** 的墓碑键扫描门 —— 但那是第三个决定,不塞进本单。
现场
content/docs/ui/apps.mdx:17 / :45 / :215-224 / :235
packages/spec/src/ui/app.zod.ts:1099(version 墓碑)/ :1245(mobileNavigation 墓碑)
packages/spec/scripts/check-skill-examples.ts:250-263(示例被逐字写盘后仅跑 tsc,不做 schema 校验)
在 #5280(
app.form.ts提供墓碑键)的全量扫尾中顺带发现,与该单同类不同面 —— 那单的范围是packages/spec/src/**/*.form.ts与 #3786 对账门,手写文档面不在其内,故单独立案。事实
content/docs/ui/apps.mdx有三处仍把 17.0.0 已退役的键当作可作者化面在教:os:check块内)version: '1.0.0'App.version是retiredKey()墓碑version/string/ optional / App version## Mobile Navigation,含mobileNavigation: { mode, bottomNavItems }示例与mode取值说明App.mobileNavigation是retiredKey()墓碑(完全未实现)os:check块内)version: '2.0.0'version墓碑是
z.never().optional(),所以照抄不是「多写个没用的键」,是整条 save 硬失败。实测(worktree @ origin/main de113a4,把第 12 行os:check块的对象原样喂给getMetadataTypeSchema('app')):为什么门没开火
两个示例块都带
{/* os:check */},check:skill-examples也确实在跑(本机✅ 204 prose examples type-check against @objectstack/spec)。它绿是因为这个门只做 tsc,而两个块都是无类型标注的对象字面量(const crmApp = { … }/const projectApp = { … }):没有任何东西把它们和AppSchema关联起来,retiredKey()赖以在编译期开火的never入参类型于是永远不参与推断。即:
os:check覆盖的是「示例能否编译」,不是「示例是否合规」。对退役键这一类,前者恒真。两条可能的收紧路径(择一,需裁决):const crmApp: App = { … }或defineApp({ … })),让never入参在 tsc 就红。改动面是文档示例,门本身不动。check:skill-examples对能推断出 schema 的块追加一次safeParse。覆盖更广(标注漏了也能抓),但要先解决「哪个块对应哪个 schema」的归属问题。我倾向 A + 逐步 B:A 立刻可做且零门改动;B 是长期正确的那一半,但归属推断本身需要设计。
顺带
同类不同页已有 #5291(widget-contract 的 Theme 段在教 #3494 删掉的
density)。两单的共同形状是「手写文档面没有随退役一起清」,若要一次性收口,值得考虑一条覆盖content/docs/**的墓碑键扫描门 —— 但那是第三个决定,不塞进本单。现场
content/docs/ui/apps.mdx:17/:45/:215-224/:235packages/spec/src/ui/app.zod.ts:1099(version墓碑)/:1245(mobileNavigation墓碑)packages/spec/scripts/check-skill-examples.ts:250-263(示例被逐字写盘后仅跑 tsc,不做 schema 校验)