fix(platform-objects): 21 zh-CN metadata-form leaves say what their source says - #16226
Conversation
…ource says The 613 LEGACY-TRUSTED zh-CN leaves in `zh-CN.metadata-forms.generated.ts` — differing from `en`, carrying no digest — were imported from a pre-consolidation hand vocabulary in `e0077ea36` and never reconciled against the seeded English. The census on #14587 read all 613 and found 26 that assert something the source does not, or drop a distinct concept the source names. Five (the whole `dashboard` subtree) landed in `9f57f1e31`; these are the remaining 21, verified still wrong against `origin/main` `9f3989771` before any edit. No instrument here can see them, by construction: `check-i18n-stale-fill` detects a byte copy of a previous source revision, and these are hand-written mistranslations. Merge does not correct them either — it fills gaps only. Each replacement is argued against this bundle's own usage rather than freshly invented: `action.fields.body.helpText` takes the sibling `hook.fields.body.helpText` verbatim for the identical source sentence; 「环境助手」 is the bundle's own rendering of "ambient assistant"; 「请求体结构」 is its own for "request shape"; 「按对象 / 按字段」 comes from the two fields the permission section holds; 「分块」、「维度」、「度量」 come from the report subtree; and the no-trailing-period register follows 60 of 66 translated section descriptions. zh-CN only. es-ES and ja-JP are not touched, nor are the 18 looser paraphrases the census deliberately excluded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 138eb98e0bb4839ca31019de3009a0385bf098d1 && git checkout 138eb98e0bb4839ca31019de3009a0385bf098d1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cbca47d09bc00b237f1f75046f03205dd4da2d32 7b890a69eba38e24172cfdcff16142e1b8bb05ec && git checkout -B drift-repro cbca47d09bc00b237f1f75046f03205dd4da2d32 && git merge --no-ff 7b890a69eba38e24172cfdcff16142e1b8bb05ec
node scripts/docs-audit/affected-docs.mjs --json cbca47d09bc00b237f1f75046f03205dd4da2d32
|
Fixes #14931
Twenty-one zh-CN
metadataFormsleaves inpackages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.tsassertsomething their English source does not, or drop a distinct concept it names. One line
each; the file's structure, its key set and its 158 byte-identical-to-
enfills areuntouched.
Authored by Claude Code in session
session_01ARYe3yQTQCUFm5qPYNgKaJ, dispatched onissue #14931; the claim record is comment 5557212975 on that card.
Subtraction first — 21 of the enumerated 26, measured before any edit
The census on #14587 (comment
5524387962) enumerated 26 leaves. Five have alreadylanded, all in the
dashboardsubtree, all in one commit9f57f1e31(#14926). Bothbundles were flattened to dotted leaf paths at
origin/main9f3989771: 773 leaveseach, key sets identical, 158 byte-identical to
en, 615 differing (the census read767 / 154 / 613 — the bundle has gained six leaves since). Every one of the 26 was then
compared, by text, against that tree.
9f3989771dashboard.sections.basics.descriptiondashboard.sections.layout.descriptiondashboard.sections.widgets.descriptiondashboard.sections.filters.descriptiondashboard.fields.header.helpTextdashboard.fields.gap.helpText, repaired by #16163, is not one of the 26 and doesnot move the count. The remaining 21 are this PR, and every one still carried its
census value at
9f3989771.⛔ The 18 looser paraphrases the census deliberately excluded are untouched, and so are
es-ES and ja-JP.
Grouped by defect kind, with the source quoted
A · Asserts an input or a concept that does not exist
en)skill.sections.triggers.descriptionemail_template.fields.variables.helpText[{ "name": "user.name", "type": "string", "required": true, "description": "..." }][{ "name": "user.name", … }]action.sections.advanced.descriptionpage.sections.basics.descriptionemail_template.sections.identity.descriptionapp.fields.defaultAgent.helpTextskill.sections.triggersholds exactly one field,triggerConditions(
packages/spec/src/ai/skill.form.ts:35-42).triggerPhraseswas removed with the key —the form's own comment records that phrases were never matched. 「触发关键词」 promised a
keyword input that is not there.
email_template.fields.variables—EmailTemplateDefinitionVariableSchema(
packages/spec/src/system/email-template.zod.ts:37-42) declaresname/type/required/description. There is no default value. The leaf promised「默认值」. The replacement follows the sibling
email_template.fields.fromOverride.helpText,which is already 「示例:
{ "name": "Acme Sales", … }」 — the bundle's own way oftranslating a helpText that IS a JSON literal.
action.sections.advanced—bulkEnabledwas removed from this section(
packages/spec/src/ui/action.form.ts:81-82); it now holdsai,recordIdParam,recordIdField,bodyShape. 「批量」 named a control that is gone, while 「API 请求体结构」 —the exact phrase the bundle already uses at
action.fields.bodyShape.label— was missing.page.sections.basicsholdsname,label,icon,type,template,description; 「名称、标题与图标」 enumerated three of six and droppedtemplate, the oneconcept the source raises to section level. 「页面标识与模板」 mirrors the landed
「仪表板标识与描述」.
app.fields.defaultAgent— the source names no button and no screen position;「右下角浮动助手按钮」 invented both, and the
'ask'/'build'defaults were missing.「环境助手」 is this bundle's own rendering of "ambient assistant"
(
app.sections.content.description), and 「智能体」 its own for "agent"(
app.fields.defaultAgent.label).B · Names the wrong technology
en)action.fields.body.helpTextpage.fields.type.helpTextaction.fields.bodytold a zh author the body is JavaScript. An L1 expression is notJavaScript, and the L2 body is sandboxed. The replacement is verbatim the sibling
hook.fields.body.helpText, which translates the identical source sentence correctly —⛔ not a third phrasing invented here.
page.fields.typecarried a superseded enumeration (list / record / home / app / utility) that the source no longer names, and dropped the claim it does make — that"List / Interface" binds a source view into a curated surface. The option labels are not
in this bundle, so the author sees
List / Interface pagein English; the quoted token istherefore kept in English too. The trailing sentence was already correct and is carried
through unchanged.
C · Drops a distinct concept the source names
en)object.fields.isSystem.helpTextobject.fields.fields.trackHistory.helpTextview.fields.filter.helpTextpage.sections.advanced.descriptionreport.sections.basics.descriptionreport.sections.joined_blocks.descriptionreport.fields.columns.helpTextemail_template.sections.subject.descriptionpermission.sections.identity.descriptionpermission.sections.system_permissions.descriptionpermission.sections.object_and_field_permissions.descriptionpermission.fields.systemPermissions.helpTextskill.fields.description.helpTextTwo of these were saying something the code contradicts rather than merely saying less:
skill.fields.descriptionclaimed the field is what the AI reads to decide whether toinvoke the skill — the form's own comment records that routing is
triggerConditionsplusthe agent allowlist — and
permission.fields.systemPermissionsreplaced "capability keys"with a three-way categorisation the source never draws.
Word choices, argued against this bundle's own usage
Every replacement re-uses a term this bundle already uses for the same concept, rather than
a fresh one. Counts are over the 773 zh-CN leaves of this file.
object.sections.basics「标识、显示名与分类标签」; the landed 「仪表板标识与描述」app.sections.basics.descriptiontranslates the same English word "activation" as 「启用状态」page.fields.aria.label、dashboard.sections.advanced.descriptionapp.sections.content.descriptionaction.fields.bodyShape.labeldashboard.fields.globalFilters.helpTextpermission.fields.objects.helpText、permission.fields.fields.helpText— the two fields this very section holdspermission.sections.tab_and_row_level_security.labelis already 「标签页与行级安全」; 字段级安全 is its exact parallelreport.fields.blocks.label、report.fields.order.helpTextjoined/matrixreport kindsreport.fields.type.helpText「报表类型:tabular / summary / matrix / joined」examples/app-showcase/src/system/translations/index.ts,packages/lint/src/showcase-shape.fixtures.ts)Three calls needed a decision rather than a lookup, and are recorded as such:
bundle by roll-up aggregation (
field.fields.summaryOperations.label「汇总操作」), and摘要 is used elsewhere in the repo for messaging digests
(
service-messaging/src/translations/zh-CN.objects.generated.ts). Both would be falsefriends here.
运算符and操作符both return 0 hits acrosspackages/,examples/); the standard term was taken.{{var.path}}token is kept verbatim, which is what actually tells the author what to type.Punctuation register, measured, not assumed: 60 of the 66 translated section
descriptions in this file do not end in
。, and 205 of 212 translated helpTexts do noteither; the six that do are multi-sentence. Single-clause replacements here therefore carry
no trailing period, and the five multi-sentence ones do.
⛔ Every instrument here is blind to this class — the gates are reported, not claimed
check-i18n-stale-filldetects a leaf that is a byte copy of a previous sourcerevision, via cross-locale agreement or a recorded digest. These 615 legacy-trusted leaves
are neither, and the gate's own header states the general case: once a leaf differs from
current source, a stale fill is indistinguishable by value from a real translation. The
card proved this by ablation. ⇒ No green below is evidence that this change was needed or
that it worked. The acceptance evidence is the leaf-by-leaf table above.
Regeneration does not correct these either: bundle merge fills gaps only, and a
present-but-wrong leaf is not a gap.
Gate family — all 49 derived families, run at
7b890a69eDerived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no hand-written path list): 40 named by path + 2 by change kind + 7 declared whole-tree.
All 49 exit 0. Exit codes were captured with redirect-then-read, never through a pipe.
Two returned exit 3 = PREREQUISITE NOT MET on the first pass —
check:i18n(workspaceCLI not built) and
check:dual-build-cjs-loads(packages withoutdist/). Those areNOT MEASURED, not reds. Both prerequisites were then cleared with a real build and both
re-run green:
check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys).check:dual-build-cjs-loads — 103 published require entry point(s) across 66 package(s) load; 619 emitted CommonJS file(s) parsecheck-i18n-stale-fill: OK (10 bundle set(s) — no new stale fills, 0 baselined).· 27 provenance companions, 27 served, 0 unservedcheck-dts-closure: 57 built package(s) swept - 147/147 declared declaration file(s) present— andpackages/platform-objects/distis in that swept set (verified: the 8-package first pass was exactly the 8 directories carrying adist/, platform-objects among them)Package-level:
@objectstack/platform-objects— 35 test files, 528 tests, all passing;typecheckclean, andtsc --listFilesconfirms the edited file is in that program(1 hit), so the green is about this file rather than around it.
Repo-wide
pnpm lintwas narrowed, and the narrowing is measured rather than assumed:(1)
eslint --print-configon the edited file returns 6 configured rules, so it is ineslint's own swept population; (2)
--format jsonover the diff reports 2 files handed in,1 actually linted (the changeset
.mdis "ignored because no matching configuration"),0 errors, 0 warnings; (3)
languageOptions.parserOptions.projectandprojectServiceare both
null— type-aware linting is not enabled, so editing string values in onefile cannot move the verdict on any untouched file; (4) instrument liveness — a control
file placed in the same directory under the same config produced 1
no-restricted-importserror, so the zero above is a reading and not a silence.
Clause-② —
no, and non-vacuously soMeasured by ablation at
7b890a69e, not asserted.@objectstack/platform-objectspublishesfiles: ["dist", …], so the published surface is all 66 files underdist/— the wholeset was compared, ⛔ not the root barrel.
Result across the ablation:
dist/**/*.d.tsand*.d.mts, everyentry point plus every subpath —
apps,audit,identity,integration,metadata,metadata-translations,pages,plugin,security,system, root).index.js,index.mjs,metadata-translations/index.js,metadata-translations/index.mjs,plugin.js,plugin.mjsand their six sourcemaps.dist/metadata-translations/index.js: exactly 42 changed lines= the 21 pairs, every one a string-literal value. No declaration, no export, no
signature. (esbuild emits the CJK as
\uXXXX; two hunks switch the surrounding quotefrom
"to'because the value itself contains"— still a value.)⇒ The declaration surface is provably still, while the runtime content provably moved. Per
the standing #16076 ruling, a string-literal VALUE inside an emitted data type is not limb-1
movement.
Changeset —
patch, re-derived rather than takennpm pack --dry-run --jsoninpackages/platform-objects: not private, 69 entries, 66dist/files in the tarball and 0src/files;dist/index.js,dist/metadata-translations/index.js,dist/plugin.jsanddist/index.d.tsare all in it.Scanning the 22 published JS files for each new value in its emitted
\uXXXXform:21 / 21 present, 6 files each, and the old
action.fields.bodyvalue is present in 0.⇒ Published content changes for a zh-reading Studio author ⇒
patch, ⛔ notskip-changeset.No API surface moves ⇒ ⛔ not
minor.objectui overlay — checked per leaf, none of the 21 is shadowed
packages/app-shell/src/views/metadata-admin/metadata-form-i18n.tsat objectuiorigin/main3197379resolvesbundleLocale()to'zh-CN'for zh, so a zh leaf CAN beshadowed there. Its
METADATA_FORM_BUNDLEdeclares exactly one type key —dashboard(measured: 1 hit at the type indent, with a firing control returning 2 at the next indent
down). Every leaf this PR touches is under
object,view,page,app,action,report,email_template,permissionorskill, so none of the 21 is shadowed, and⛔ nothing in objectui is edited here. That is also why the five already-landed leaves were
the ones that needed overlay matching and these do not.
Reported, not repaired here
was established at two leaves and does not generalise. Measured across the 21: es-ES and
ja-JP carry the same defect at nine of them, including the two the card singles out —
action.fields.body.helpTextreadsCódigo JavaScript que ejecutar/ 「実行する JavaScriptコード」 in exactly the way zh-CN did. Filed separately with the table rather than widened
into here; ⛔ those files are also PR #16190's right now.
Related: #14587 (the census, comment
5524387962) · #14926 · #16163 · #16190.🤖 Generated with Claude Code
https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
Generated by Claude Code