feat(spec)!: translation 两个门都关上,#3778 的十键守卫退役进错误信息(#4001 批 5) - #4529
Merged
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
os-zhuang
marked this pull request as ready for review
August 1, 2026 17:43
os-zhuang
enabled auto-merge
August 1, 2026 17:43
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…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
This was referenced Aug 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#4001 批 5(清单)。批 1–4 = #4514 / #4519 / #4522 / #4527 / #4528,均已合并。
为什么 translation 是这场战役里最狠的一个
一条解析不出来的翻译,和一条根本没人写的翻译,长得一模一样。
屏幕上不会出现错的字符串——只会一直是源语言。没有错误、没有日志,也没有任何东西能把「还没翻」和「翻了但键名写错、永远读不到」区分开。所以这个 bug 会一直被当成「翻译覆盖率不够」,永远排在待办里。
#3778 已经知道这件事了,而且为十个键修过
退役 object-first(
o.<object>)方言时踩的正是这个坑:老形状的 item 存进去干干净净,然后什么都解析不出来。当时的修法是一个z.preprocess,扫描十个退役键,各自报一条带正确去处的 422。它有效,而且它有每一个「绕开
.strip」的补丁都有的形状:object写成objects、message写成messages、凭空发明一个组——照样静默丢弃。TranslationItemSchema(Studio / 元数据 API)上跑。同样这十个键写进文件式 bundle——也就是 examples 和平台应用真正在用的那条路——一声不吭地被剥掉。和 #4522 在 #1535 的 object 守卫上发现的,是同一种不对称:一扇门关着,另一扇敞着,两个作者各自解决眼前的问题。
形状关上之后这个守卫就是多余的,于是它退役了,十条处方改挂在拒绝信息的
guidance上。这里的普遍结论是:覆盖范围
对象/字段/视图/动作/分区翻译 · apps 与导航 · dashboards 与 widgets · pages · settings · metadataForms · i18n 配置。
别名表补上编辑距离够不到的那些:
views/actions/sections_views/_actions/_sections_前缀正是这套约定本身helphelpText(动作参数上)help在字段翻译和设置项上是对的——隔壁表面借来的词labeltitle(widget 上)label,它的 widget 是title——同一份文档、差一层、拼法相反i18n 配置里 #3494 删掉的四个旋钮补上墓碑。
fileOrganization、messageFormat、lazyLoad、cache当初被删是因为没有任何运行时读它们。删掉一个本来就是 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 },而这个类型两个都没声明——于是那份权威创建形状里三分之二被静默剥掉,而专门用来抓这件事的闸门报绿。
一个建在
.stripschema 上的闸门,能抓「缺了必填键」,永远抓不到「多了未声明键」。它一直只干了一半的活,读起来却像干全了。修法是声明
name/label——25 个注册类型里translation是唯一没有name的,这种不规则正是 AI 作者会踩的——并在 liveness 账本里如实标为 dead 的 body 键(活的是行上的那一列)。进度
注册类型顶层已关闭:17 / 25
仍剥离:
action·agent·dashboard·field·mapping·page·view验证
@objectstack/spec:284 文件 / 7208 用例通过,tsc --noEmit干净check:*全绿(含check:liveness、check:i18n-coverage12 份真实 config).parse()通过:examples/app-crm、examples/app-todo、platform-objects;全部 examples 构建通过GET /translations/:locale响应体在收紧后的 schema 下仍符合(runtime i18n conformance)授权影响:这些形状没声明的键从「静默丢弃」变成「拒绝」——它本来就已经被忽略了,所以没有任何在工作的翻译会变。
参考
objects.<name>vs the runtimetranslationmetadata type'so.<object>, so translations cannot be authored in the product #3778(object-first 方言退役)、feat(spec)!: ObjectSchema 在 parse 路径上收紧,而不只是 create() —— #1535 的奠基例子一直是活的(#4001 批 3a) #4522(Object-levelworkflows: [...](and any unknown ObjectSchema key) is silently stripped at build — no error/warning (ADR-0032 'no silent failure', metadata layer) #1535 的同形不对称)、[P2] Aspirational-config disposition: reconcile-or-prune the still-dead props from the 2026-06 liveness audit (Theme / Translation / Job / Webhook) #3494(删掉的四个旋钮)🤖 Generated with Claude Code
https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY
Generated by Claude Code