Skip to content

feat(spec)!: translation 两个门都关上,#3778 的十键守卫退役进错误信息(#4001 批 5) - #4529

Merged
os-zhuang merged 2 commits into
mainfrom
claude/strict-schema-authz-surface-s8vnok
Aug 1, 2026
Merged

feat(spec)!: translation 两个门都关上,#3778 的十键守卫退役进错误信息(#4001 批 5)#4529
os-zhuang merged 2 commits into
mainfrom
claude/strict-schema-authz-surface-s8vnok

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#4001 批 5(清单)。批 1–4 = #4514 / #4519 / #4522 / #4527 / #4528,均已合并。

为什么 translation 是这场战役里最狠的一个

一条解析不出来的翻译,和一条根本没人写的翻译,长得一模一样。

屏幕上不会出现错的字符串——只会一直是源语言。没有错误、没有日志,也没有任何东西能把「还没翻」和「翻了但键名写错、永远读不到」区分开。所以这个 bug 会一直被当成「翻译覆盖率不够」,永远排在待办里。

#3778 已经知道这件事了,而且为十个键修过

退役 object-first(o.<object>)方言时踩的正是这个坑:老形状的 item 存进去干干净净,然后什么都解析不出来。当时的修法是一个 z.preprocess,扫描十个退役键,各自报一条带正确去处的 422。

它有效,而且它有每一个「绕开 .strip」的补丁都有的形状

  • 它只能抓住有人已经想到过的错误。 object 写成 objectsmessage 写成 messages、凭空发明一个组——照样静默丢弃。
  • 它只守了两扇门里的一扇。 只在 TranslationItemSchema(Studio / 元数据 API)上跑。同样这十个键写进文件式 bundle——也就是 examples 和平台应用真正在用的那条路——一声不吭地被剥掉。

#4522#1535 的 object 守卫上发现的,是同一种不对称:一扇门关着,另一扇敞着,两个作者各自解决眼前的问题。

形状关上之后这个守卫就是多余的,于是它退役了,十条处方改挂在拒绝信息的 guidance 上。这里的普遍结论是:

一个 bespoke 守卫里值钱的从来不是检测,而是那句话。 默认一翻,检测自动泛化到所有键;而「你这段内容该放哪」那句话不会——那才是这些补丁真正在扛的东西。

覆盖范围

对象/字段/视图/动作/分区翻译 · apps 与导航 · dashboards 与 widgets · pages · settings · metadataForms · i18n 配置。

别名表补上编辑距离够不到的那些:

写成 实际是 为什么距离算不出来
views / actions / sections _views / _actions / _sections _ 前缀正是这套约定本身
help helpText(动作参数上) help字段翻译设置项上是对的——隔壁表面借来的词
label title(widget 上) dashboard 的标题是 label,它的 widget 是 title——同一份文档、差一层、拼法相反

i18n 配置里 #3494 删掉的四个旋钮补上墓碑。 fileOrganizationmessageFormatlazyLoadcache 当初被删是因为没有任何运行时读它们。删掉一个本来就是 no-op 的键,作者拿到的还是同样的沉默,只是多了一个理由。 现在拒绝信息会说是哪个 issue 删的、为什么。

两个闸门被发现只干了一半的活

1. ADR-0010 保护信封。 loader 会盖 _packageId/_provenance,schema 存不下,所以每次解析都掉;authored-translation-sync 只好在读侧手工把它们剥出来。现在声明了,欠债清单从 5 降到 4

2. metadata-create-seeds.test.ts —— 「设计器创建形状 ≠ spec 要求」的规范守卫。 它断言每个 seed 都能通过解析。translation 的 seed 发的是 { name, label, locale, objects },而这个类型两个都没声明——

于是那份权威创建形状里三分之二被静默剥掉,而专门用来抓这件事的闸门报绿。

一个建在 .strip schema 上的闸门,能抓「缺了必填键」,永远抓不到「多了未声明键」。它一直只干了一半的活,读起来却像干全了。

修法是声明 name/label——25 个注册类型里 translation 是唯一没有 name 的,这种不规则正是 AI 作者会踩的——并在 liveness 账本里如实标为 dead 的 body 键(活的是行上的那一列)。

进度

注册类型顶层已关闭:17 / 25

仍剥离:action · agent · dashboard · field · mapping · page · view

验证

  • @objectstack/spec284 文件 / 7208 用例通过tsc --noEmit 干净
  • 8 个生成物闸门 up-to-date15 个 check:* 全绿(含 check:livenesscheck:i18n-coverage 12 份真实 config)
  • 真实 bundle 全部在模块加载时 .parse() 通过examples/app-crmexamples/app-todoplatform-objects;全部 examples 构建通过
  • 线上 GET /translations/:locale 响应体在收紧后的 schema 下仍符合(runtime i18n conformance)

授权影响:这些形状没声明的键从「静默丢弃」变成「拒绝」——它本来就已经被忽略了,所以没有任何在工作的翻译会变。

参考

🤖 Generated with Claude Code

https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY


Generated by Claude Code

…ard retires into the message (#4001)

A translation that resolves to nothing is indistinguishable from a translation
nobody wrote — no wrong string appears, just the source language, forever. So
this type had the most literal version of the silent-strip failure in the spec.

#3778 already knew that, and fixed it for ten keys: a `z.preprocess` scanning
for the retired object-first dialect. It had the shape every workaround for
`.strip` has — it caught only the mistakes someone had already thought of, and
it ran on the item door only, so the same ten keys in a file-authored bundle
were dropped in silence. Same asymmetry #4522 found in #1535's object guard.

The guard is now redundant and gone; its ten prescriptions ride the rejection
as `guidance`. What was worth keeping was never the detection — detection
generalizes for free once the default flips — it was the prose.

Closed across every authorable group (objects/fields/views/actions/sections,
apps, dashboards, pages, settings, metadata forms) and the i18n config, whose
four #3494-removed knobs get tombstones.

Two gates were found doing half their job:

  - `translation` came off the ADR-0010 envelope debt list (down to four).
  - `metadata-create-seeds.test.ts` — the canonical "create shape ≠ spec"
    guard — asserts every seed parses. The `translation` seed ships
    `{ name, label, locale, objects }` and the type declared neither `name`
    nor `label`, so two thirds of the authoritative create shape was stripped
    while the gate reported green. A gate on a `.strip` schema catches a
    missing required key and never an extra undeclared one.

Registered types closed at the top level: 17 of 25.

Verified: 284 files / 7208 tests, tsc clean, 8 generated artifacts current,
15 check gates green, and the real bundles in examples/app-crm, app-todo and
platform-objects all parse at module load.

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

vercel Bot commented Aug 1, 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 1, 2026 5:45pm

Request Review

@os-zhuang
os-zhuang marked this pull request as ready for review August 1, 2026 17:43
@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:system tests tooling size/l labels Aug 1, 2026
@os-zhuang
os-zhuang enabled auto-merge August 1, 2026 17:43
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @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 packages/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/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/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/kernel/runtime-capabilities.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/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.

…just the metadata one (#4001)

Three passages described #3778's guard accurately for what it was — ten keys,
rejected at the metadata door — and #4001 changed both halves of that: any
undeclared key is rejected, in a runtime item and in a file-authored bundle.

- ui/translations.mdx: "only the groups on this page are accepted" was
  aspirational for bundles; it is now literally true. Says why this surface
  cares more than most — a translation that resolves to nothing looks exactly
  like one nobody has written yet.
- i18n-standard.mdx: same correction on the retired-dialect callout.
- i18n-standard.mdx: the `translationService` design-intent callout already
  warned the key is unrecognized; copying that snippet is now a build-time
  rejection rather than a silent drop, which is the part a reader acts on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants