Skip to content

fix(spec): app 表单摘掉八个已退役的墓碑键输入,#3786 对账门改判「真实可授权面」(#5280) - #5318

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-5280-form-tombstone-reconciliation
Aug 4, 2026
Merged

fix(spec): app 表单摘掉八个已退役的墓碑键输入,#3786 对账门改判「真实可授权面」(#5280)#5318
os-zhuang merged 3 commits into
mainfrom
claude/issue-5280-form-tombstone-reconciliation

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5280

前提核验(origin/main @ de113a4,带阳性对照)

三条都成立,但范围比单子写的大:issue 点名三个键(sharing / embed / mobileNavigation),按范围第 3 条全量扫下来,app.form.ts 一共在提供八个 retiredKey() 墓碑键。

app.sharing:          in shape=true innerType=never isRetired=true
app.embed:            in shape=true innerType=never isRetired=true
app.mobileNavigation: in shape=true innerType=never isRetired=true
app.navigation:       in shape=true innerType=union isRetired=false   ← 阳性对照
app.label:            in shape=true innerType=string isRetired=false  ← 阳性对照

form offers ∩ retired: ["version","homePageId","mobileNavigation","objects","apis","sharing","embed","aria"]

safeParse sharing:          success=false :: `App.sharing` was removed in @objectstack/spec 17.0.0 …
safeParse embed:            success=false :: `App.embed` was removed in @objectstack/spec 17.0.0 …
safeParse mobileNavigation: success=false :: `App.mobileNavigation` was removed in @objectstack/spec 17.0.0 …
control (no retired key):   success=true

另外五个(version / homePageId / objects / apis / aria)与点名的三个同类同因:都是 17.0.0 的 retiredKey() 墓碑,处方都落在别的层(manifest.version;navigation 首项 + isDefault;defineStack({ objects });defineStack({ apis });渲染 DOM 的组件),没有一个部分映射到 App 自身的活键,所以八个的摘除都是机械操作,按范围第 3 条「发现的一并修」同 PR 处理。

1) 表单(范围 1+3)

八个 { field: … } 输入全部移除,按套件惯例在原位留注释说明能力去向。另有两个分区描述本身还在宣传已退役能力(Content 的 "Objects and APIs this app uses."、Access & sharing 的 "…and how it can be embedded."),一并订正为真实语义。

2) 门谓词(范围 2 —— 本单真正的价值)

#3786 的断言用 key ∈ shape 判「Zod accepts」。retiredKey() 按设计把键留在 walked shape 里(退役套件的 liveness 行正因为键还在才保留),于是墓碑键对该断言永远为真 —— 门绿着,作者拿到 422。

谓词收紧为「∈ shape 且不是墓碑」,双向都改:

  • form-only 拆成两条独立断言,失败信息分得清「未声明 → 静默丢弃」与「墓碑 → 硬失败」。后者更响也更早,原断言的措辞("silently stripped on save")只覆盖了两种失败里的一种。
  • zod-only 把墓碑键排除出「必须提供或立 ledger」的集合 —— 墓碑键的唯一正确处置就是不提供。此前 object.fields.conditionalRequired 没被要求回填,纯属被一条无关的 subset ledger 条目挡住,是运气不是设计。
  • ledger 自检(omit 的存在性、subset 的非空性)改用 authorable 键计数,免得墓碑撑住一条已失效的豁免。

检测器判 schema 节点(optional 内层是否 z.never),不匹配键名 —— 键名清单本身就是 #3786 要消灭的手抄列表。这是 build-schemas.ts 里 JSON Schema 侧 isRetired()({ "not": {} })的 zod 侧孪生。union 取安全方向:只要有一个成员仍把该键声明为活键,就不算退役。

3) RED-first 实证(方向先预测,后运行)

预测:谓词收紧、表单未改时,app 那条用例,恰好点名八个键;其余类型与两条 nested 方向不变。实测一致:

 ❯ src/system/metadata-form-zod-reconciliation.test.ts (41 tests | 1 failed)
     × app: every field the form offers is a key the author may write

AssertionError: app: offered by the form but RETIRED in the Zod (retiredKey tombstone —
filling the control hard-fails the save)…: expected [ 'apis', 'aria', 'embed', …(5) ] to deeply equal []
+   "apis", "aria", "embed", "homePageId", "mobileNavigation", "objects", "sharing", "version"

 Tests  1 failed | 40 passed (41)

摘掉输入后 41 passed (41)

自检钉在合成 schema 上而非当期某个墓碑:墓碑会随版本退场(retired-key.ts 写的约两个大版本),锚在活墓碑上的自检要么烂尾,要么在锚被删的那一版悄悄空转 —— 一条什么都不过滤却依旧报绿的门。四条自检覆盖:墓碑留在 shape 里(即 key ∈ shape 为何是错谓词)、值被硬拒且带处方、按节点而非键名判定、union 成员仍 live 时不算退役。

4) 全量扫(范围 3)

17 个 *.form.ts 全部在册(METADATA_FORM_REGISTRY 覆盖),顶层 + 手写 nested 列表一起扫。修复后:

tombstone keys still offered by ANY form: 0
app form final fields: name,label,description,icon,active,isDefault,navigation,areas,defaultAgent,branding,requiredPermissions
app tombstones still in shape (必须保留 —— 墓碑路线): version,homePageId,objects,apis,sharing,embed,mobileNavigation,aria

其余带墓碑的类型(field / view / dashboard / action / agent / skill / flow)的表单没有在提供它们,本单无需改动。未删除任何墓碑,只删表单控件 —— liveness 行依赖键继续留在 shape 里。

5) i18n

表单输入变动会带走抽取键,四个 *.metadata-forms.generated.ts 按 merge 模式重生成(纯删除,每个 bundle 8 组 field 键)。两个分区描述的 leaf 值按「bundle 值可手写」的惯例手工订正 —— merge 模式不会用源文覆盖已有值,不改的话渲染出来的仍是旧文案(zh-CN 的 basics/navigation 甚至点名「版本」「首页」两个已退役键)。

验证

命令 结果
vitest run src/system/metadata-form-zod-reconciliation.test.ts 41 passed (41)
pnpm --filter @objectstack/spec test 308 files / 7959 tests passed
pnpm --filter @objectstack/spec check:generated 9/9 up to date
pnpm check:i18n OK(9 包全部 in sync)
pnpm check:i18n-coverage OK(660 baselined,none new)
pnpm --filter @objectstack/spec --filter @objectstack/platform-objects typecheck Done
pnpm --filter @objectstack/cli test 69 files / 626 tests passed
pnpm --filter @objectstack/lint test 57 files / 1198 tests passed
pnpm --filter @objectstack/platform-objects test 9 files / 266 tests passed
node scripts/check-nul-bytes.mjs OK(5343 文件,无裸 NUL)

消费半径按门的调用方扫过(不是按改动的包):METADATA_FORM_REGISTRY 的消费方是 packages/cli 的 i18n 抽取/覆盖率、packages/lint 的翻译引用校验、packages/platform-objects 的 bundle,三者的测试都跑过。

git merge origin/main(合入 #5289 / #5293 / #5296 / #5302 / #5303),生成物整体重生成而非文本合并,合并后全部门重跑并确认兄弟 PR 的条目仍在。

范围外发现


🤖 Generated with Claude Code

https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB


Generated by Claude Code

claude added 2 commits August 4, 2026 18:38
`app.form.ts` 仍在给作者渲染八个 `retiredKey()` 墓碑键的输入控件 ——
`version` / `homePageId` / `objects` / `apis` / `sharing` / `embed` /
`mobileNavigation` / `aria`(issue 只点了后三个,全量扫出八个)。墓碑是
`z.never().optional()`,填进去不是静默丢弃,是整条 save 硬失败并抛出该键
的退役处方。八个控件全部移除,按套件惯例在原位留注释说明能力去向。

真正的价值在门这一侧:#3786 的对账断言用 `key ∈ shape` 判定「Zod 接受该
键」。在 `retiredKey()` 出现之前这两件事同义,之后不是了 —— 墓碑**按设计**
把键留在 walked shape 里(退役套件的 liveness 行正因为键还在才保留),于是
八个墓碑键对该断言永远为真,门绿着而作者拿到 422。

谓词收紧为「∈ shape 且不是墓碑」,双向都改:
- form-only 方向拆成两条独立断言,失败信息能分清「未声明→静默丢弃」与
  「墓碑→硬失败」,后者更响也更早;
- zod-only 方向把墓碑键排除出「必须提供或立 ledger」的集合 —— 墓碑键的唯一
  正确处置就是不提供。此前 `object.fields.conditionalRequired` 不被要求回填
  纯属被一条无关的 `subset` ledger 条目挡住,是运气不是设计;
- ledger 的 `omit`/`subset` 自检改用 authorable 键计数,避免墓碑撑住一条
  已失效的豁免。

检测器判 **schema 节点**(optional 内层是否 `z.never`),不匹配键名 —— 键名
清单本身就是 #3786 要消灭的手抄列表。这是 `build-schemas.ts` 里 JSON Schema
侧 `isRetired()`(`{ not: {} }`)的 zod 侧孪生。四条合成 schema 上的自检把
机制钉住(含 union 成员仍 live 时不算退役的安全方向),不依赖任何当期墓碑,
免得墓碑随版本退场后这条门悄悄变空转。

RED-first 实证:谓词收紧、表单未改时,`app` 那条用例红,恰好点名八个键;
摘掉输入后 41/41 绿。

表单输入变动会带走 i18n 抽取键,四个 metadata-forms bundle 按 merge 模式
重生成(纯删除 8 组);两个分区描述原文仍在宣传已退役能力(zh-CN 的
basics/navigation 甚至点名「版本」「首页」),按 bundle 值可手写的惯例一并
订正为真实语义。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 4, 2026 10:01pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:system tests protocol:ui tooling labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/spec.

107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/platform-objects, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/platform-objects, @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review August 4, 2026 22:02
@os-zhuang
os-zhuang enabled auto-merge August 4, 2026 22:02
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit f1cc3a3 Aug 4, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5280-form-tombstone-reconciliation branch August 4, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants