Commit 5757463
fix(platform-objects): re-translate the metadata-form leaves that contradict their current en source (#20707)
Fixes #20666
Clause-②: no
## What changed
A translated leaf in
`packages/platform-objects/src/apps/translations/{ja-JP,es-ES,zh-CN}.metadata-forms.generated.ts`
that a translator wrote by hand keeps its value when its `en` source
changes later. This PR measures that set first, with PR #20652's
instrument and the two widening checks the services seat named, then
re-translates the leaves whose meaning now **contradicts** the current
`en`: **12 leaves** (ja-JP 4, es-ES 4, zh-CN 4).
- Values only. No key is added or dropped, no `en` file is edited, and
no provenance companion or echo-decision ledger changes (measured below;
the tooling requires none).
- `.changeset/20666-stale-authored-metadata-form-leaves.md`:
`@objectstack/platform-objects` `patch`.
- No new gate, per triage. No test pins any of the old or new strings.
- File surface held: the three translated `metadata-forms` bundles and
the changeset. Not the object bundles, not the plugin bundles, and not
`{en,ja-JP,es-ES,zh-CN}.ts` or `*.source-hashes.ts` (PR #20699).
## The measurement (base `a8acee28d`, before any edit)
### Instruments
The clone is not shallow (`git rev-parse --is-shallow-repository`
answers `false`, 15122 commits), so every walk below reads full history.
1. **Since last edit** (PR #20652's condensed instrument, ported with
two edits: the bundle kind `objects.generated.ts` becomes
`metadata-forms.generated.ts`, and the recorded-copy test reads
`"metadataForms.PATH"` in both `LOCALE.source-hashes.generated.ts` and
`LOCALE.source-hashes.ts`). Population: every string leaf of the
locale's metadata-forms bundle, minus echoes of the current `en` and
recorded byte copies. Last edit: the first-parent commit where the
parsed value last changed. A leaf is a candidate when `en` at that
commit differs from `en` today. Meaning is then judged by hand.
2. **Renamed key.** A leaf whose path was born at its last edit, while
the same value sat under a path (any path in the bundle, not only a
sibling) that disappeared in that commit. For each, `en` at the old
path's last edit is compared with `en` at the new path today.
3. **Penultimate edit.** For each non-candidate, `en` at its previous
edit, and whether `en` moved in the last-edit commit itself. Every flag
was read by hand, including the ones where `en` moved in the same
commit.
4. **Merge side.** For a leaf last edited by a merge commit, `en` is
also read at the second parent.
5. **Retired term.** The locale's word for `project`, `department`,
`role` (ADR-0090) or `profile` (ADR-0090 D2), where `en` at the same
path does not carry the English term. The raw ja-JP `role` pattern hit
four leaves, all false positives (ロールアップ roll-up three times, コントロール
control once); the refined pattern excludes both and hits 0.
6. **Retired concept** (added here; the concept-level twin of PR
#20652's retired-term scan). For each re-model the contradicting
candidates surfaced, the locale's words for the retired concept, flagged
where `en` at the same path does not carry it: agent tools (ADR-0109
removed `agent.tools[]`, scope `agent.*`);
`PermissionSet.contextVariables` (removed by the ADR-0105 commit
`879ea1304`); sharing rules named for RLS; an email template sent by
`id` with a content type (`47a92f427` re-modelled the type); a report
block that joins objects (ADR-0021 dataset-bound reports, `18178454c`,
scope `report.*`).
7. **Carve-in continuity.** The zh-CN leaves last edited at `e0077ea36`
were carried in from a hand overlay,
`packages/platform-objects/src/metadata-translations/zh-CN.ts`, born at
`dc721729a` and consolidated at `e0077ea36` the same day. So the
instrument dates them at the carve-in. Between those two commits
`packages/spec/src` changed no form label, description or help string
(`git diff dc72172 e0077ea -- packages/spec/src`: two changed
`label` / `description` lines, both schema declarations, no string). The
carve-in dating therefore hides no source move inside the overlay's
lifetime.
### Known-positive controls
- `permission.sections.tab_and_row_level_security.description` (line
2251 of all four bundles) and `agent.sections.capabilities.description`
(line 2347). The since-last-edit instrument flags both in **all three**
locales. In ja-JP and es-ES both promise what `en` dropped (custom
context variables; tools). zh-CN's agent leaf promises tools too, but
zh-CN's permission leaf never promised context variables: it names the
section after sharing rules, read below.
- Renamed-key check: `dashboard.fields.refreshIntervalSeconds.label`,
renamed from `refreshInterval` at `e9fcd6bbd` with the value carried. It
fires in all three locales.
- Retired-concept scan: it fires on every one of the four contradicting
paths in ja-JP and es-ES, and on the three in zh-CN.
- Provenance (for H2): with ja-JP
`agent.sections.capabilities.description` set to the `en` string,
`check-i18n-bundles --filter=platform-objects` goes red,
`platform-objects DRIFTED (1)` (through `scripts/ablation-replace.mjs`:
anchor 1 to 0, blob `1b912e07c279` to `70cb7a0e1ba3`, restored: blob
equals HEAD and `git diff HEAD` is empty). In hold mode, `--write` adds
exactly one row,
`"metadataForms.agent.sections.capabilities.description"`, to
`ja-JP.source-hashes.generated.ts`. Both files were restored with `git
checkout HEAD --`, and their blobs equal HEAD's (`1b912e07c279`,
`2429910c839c`).
### Population and radius
- **Covered:** the `metadata-forms` bundles of
`@objectstack/platform-objects` for ja-JP, es-ES and zh-CN. There are
1116 leaves per locale. Echoes of the current `en` number 0 / 0 / 0,
recorded byte copies 0, and authored leaves **1116 / 1116 / 1116**.
History: every first-parent commit since the bundles were created at
`ae2da1e7d` (69 / 70 / 70 commits touching each locale file, 56 for
`en`), plus the zh-CN overlay's lifetime (instrument 7).
- **Not covered:** a leaf that was wrong when it was authored and whose
`en` never moved, outside the vocabularies of instruments 5 and 6; where
the zh-CN overlay's strings came from before `dc721729a` (the file was
born with them); the object bundles, the plugin bundles and the other
packages' bundle sets.
### Counts per locale
| | ja-JP | es-ES | zh-CN |
|---|---|---|---|
| authored leaves | 1116 | 1116 | 1116 |
| candidates (`en` moved since last edit), before | 9 | 9 | 6 |
| of which contradicting, before | 4 | 4 | 3 |
| penultimate-edit flags, before (contradicting) | 18 (0) | 18 (0) | 22
(0) |
| renamed-key flags, before (contradicting) | 1 (0) | 1 (0) | 1 (0) |
| merge-side flags, before | 0 | 0 | 0 |
| retired-term flags, before | 0 | 0 | 0 |
| retired-concept flags outside the candidates, before (contradicting) |
0 | 0 | 1 (1) |
| **re-translated here** | **4** | **4** | **4** |
| candidates after (at `aaee46778`) | 5 | 5 | 3 |
| contradicting after | 0 | 0 | 0 |
| retired-concept flags after | 0 | 0 | 0 |
The raw candidate counts equal the #20653 dev's 9 / 9 / 6 exactly.
After, both walks re-run at `aaee46778`: each after-candidate set is
exactly the before set minus the re-translated paths (0 added), the echo
count is still 0 (no new value equals its `en` leaf), and the
renamed-key flag is the same one. The penultimate-edit flags after are
the before set plus the 11 re-translated candidates (their last edit is
now this PR, after `en` moved), which is the expected shape.
### The judgment rule
As in PR #20652 and PR #20684. A leaf **contradicts** the current `en`
when a reader who acts on it would believe something about the current
form that `en` now says is false: `en` now denies what the leaf asserts;
the type was re-modelled, so the leaf describes a different thing; or
the leaf names a mechanism the section does not hold. Added detail,
narrowing, rewording, punctuation and title-casing are not
contradictions.
## Re-translated (12): before and after
**`agent.sections.capabilities.description`**, all three. ADR-0109
removed `agent.tools[]` (`e2616e0cf`); an agent's tools come from its
skills. `en`: `Skills and knowledge sources the agent can use.`
- ja-JP: 「エージェントが使用できるスキル、ツール、ナレッジソース。」 → 「エージェントが使用できるスキルとナレッジソース。」
- es-ES: 「Skills, herramientas y fuentes de conocimiento que puede usar
el agente.」 → 「Skills y fuentes de conocimiento que puede usar el
agente.」
- zh-CN: 「代理可使用的技能、工具与知识来源」 → 「代理可使用的技能与知识来源」
**`permission.sections.tab_and_row_level_security.description`**, all
three. `879ea1304` removed `PermissionSet.contextVariables`
(enforce-or-remove, zero consumers) and its form row. `en`: `Tab
visibility and RLS policies.`
- ja-JP: 「タブ表示、RLS ポリシー、述語評価用カスタムコンテキスト変数。」 → 「タブ表示と RLS ポリシー。」
- es-ES: 「Visibilidad de pestañas, políticas RLS y variables de contexto
personalizadas para evaluar predicados.」 → 「Visibilidad de pestañas y
políticas RLS.」
- zh-CN: 「导航可见性与共享规则」 → 「标签页可见性与行级安全策略」. The old value named the section
after **sharing rules**. In this repository 共享规则 is the zh-CN name of a
different mechanism, the `sys_sharing_rule` object (`plugin-sharing`)
and its own Setup entry `nav_sharing_rules`. The section holds
`tabPermissions` and `rowLevelSecurity`. The new value uses the bundle's
own words: 标签页 (this section's label), 行级安全策略 (the bundle's rendering of
an RLS policy, as in `object.fields["access.default"].helpText`).
**`report.fields.blocks.helpText`**, all three. The 9.0 single-form
cutover (ADR-0021, `18178454c`) made every report block a dataset-bound
sub-report; no block joins objects. `en`: `Dataset-bound sub-reports
(joined report only)`
- ja-JP: 「複数オブジェクトを結合(joined レポートのみ)」 → 「データセットにバインドされたサブレポート(joined
レポートのみ)」
- es-ES: 「Une varios objetos (solo informe joined)」 → 「Subinformes
vinculados a un conjunto de datos (solo informe joined)」
- zh-CN: 「joined 报表的联合查询块」 → 「绑定数据集的子报表(仅 joined 报表)」
**`email_template.sections.identity.description`**, ja-JP and es-ES.
`47a92f427` promoted `email_template` to a first-class metadata type and
rewrote its form. The Identity section now holds `name` / `label` /
`category` / `locale` / `description`, with no `id` and no content type,
and `IEmailService.sendTemplate` resolves the template by `name`. A
reader who copied the old sample would pass `template: id`. zh-CN
already said what `en` says. `en`: `Template identifier resolved by
IEmailService.sendTemplate({ template: name, locale, ... }).`
- ja-JP: 「識別子とコンテンツ型。id は sendTemplate({ template: id, ... }) で参照される。」 →
「IEmailService.sendTemplate({ template: name, locale, ... })
が解決するテンプレート識別子。」
- es-ES: 「Identificador y tipo de contenido. El id se referencia con
sendTemplate({ template: id, ... }).」 → 「Identificador de plantilla que
resuelve IEmailService.sendTemplate({ template: name, locale, ... }).」
**`report.sections.joined_blocks.label`**, zh-CN only. It was found by
the retired-concept scan, not by the since-last-edit instrument, because
`en` (`Joined blocks`) never moved. The zh-CN label said 关联对象 ("related
objects"), the pre-9.0 model in which blocks joined objects. The section
holds the `blocks` repeater of dataset-bound sub-reports. ja-JP 結合ブロック
and es-ES Bloques unidos render `en` literally and are unchanged.
- zh-CN: 「关联对象」 → 「joined 报表分块」 (分块 is `report.fields.blocks.label`, and
`joined` stays the report-type token, as in 仅 joined 报表).
## Stale but not contradicting (listed, left as written)
| path | locales | what `en` did |
|---|---|---|
| `object.sections.fields.description` | all three | "each row becomes a
column" became "each entry becomes a column" (`9f8ec35c8`). Rewording. |
| `hook.fields.condition.helpText` | all three | "Optional formula —
skip the hook when this evaluates to false" became "CEL predicate — the
hook runs only when TRUE" (`48efe915b`, which changed the row's editor
language from `javascript` to `expression`). The runtime gate is `if
(!conditionFn(ctx))` in `packages/objectql/src/hook-wrappers.ts`, whose
own docblock says "skip when the formula evaluates FALSE", and the leaf
makes no JavaScript claim. This is the closest call among the leaves
left alone. |
| `app.fields.defaultAgent.helpText` | ja-JP, es-ES | "AI agent" became
"Platform agent", with the `ask` / `build` defaults added. Narrowing and
detail. zh-CN already says what `en` says. |
| `report.sections.joined_blocks.description` | ja-JP, es-ES | "blocks
joined into a single report" became "dataset-bound blocks stacked into a
single report". Combined into one report is still true, and the `en`
label still says Joined blocks. The second closest call. zh-CN already
says what `en` says. |
| `report.sections.filter_and_chart.description` | all three |
"Report-level filters" became "Render-time scope filter". The filter is
still the report's. |
| `dashboard.fields.refreshIntervalSeconds.label` (renamed key) | all
three | The rename added the unit to the label. Each locale's `helpText`
on the same field already says seconds (自動更新(秒), Actualización
automática (segundos), 自动刷新间隔(秒)). |
Penultimate-edit flags, all read by hand. Every one already says what
`en` says:
- `en` moved EARLIER than the last edit:
`object.fields.isSystem.helpText`,
`object.fields.fields.reference.helpText`,
`object.fields.fields.trackHistory.helpText`,
`view.fields.filter.helpText`, `page.fields.type.helpText`,
`dashboard.fields.gap.helpText`, `action.sections.advanced.description`,
`action.fields.body.helpText`, `report.sections.basics.description`,
`report.fields.columns.helpText` and `dataset.fields.measures.helpText`
(all three locales); plus, zh-CN only,
`app.fields.defaultAgent.helpText`,
`report.sections.joined_blocks.description`,
`email_template.sections.identity.description` and
`email_template.fields.variables.helpText`.
- `en` moved IN the last-edit commit:
`object.fields.indexes.fields.helpText`,
`field.fields.precision.helpText`, `field.fields.maskingRule.helpText`,
`page.fields.variables.source.label`,
`app.sections.content.description`,
`app.sections.access_and_sharing.description` and
`permission.sections.identity.description` (all three locales).
## Dispatch hypotheses, measured
- **H1 held, and widened.** The targets are exactly the three translated
bundles against `en.metadata-forms.generated.ts`. Both known leaves are
candidates in all three locales, and the raw counts reproduce as 9 / 9 /
6 at `a8acee28d`. Judged, 4 / 4 / 3 contradict. Two more paths besides
the known two contradict: `report.fields.blocks.helpText` (all three)
and `email_template.sections.identity.description` (ja-JP, es-ES).
zh-CN's permission leaf contradicts for a different reason than the
known one (sharing rules). The retired-concept scan added one zh-CN
label.
- **H2 held: values only.** The companions work as they do for the
object bundles. `collectFilledFromHashes` records a `metadataForms` leaf
only while it is a byte copy of the current `en` or of a recorded
digest. Today that is 0 rows in all three locales, and a hand
translation records nothing. At `aaee46778`, `node
scripts/check-i18n-bundles.mjs --write --filter=platform-objects`
printed `regenerated` and rewrote all eleven bundle and companion files
on disk (every mtime moved), and `git status --porcelain` stayed empty.
The control that shows the tool really reads these leaves is in
"Known-positive controls" above. No echo-decision ledger row and no test
names any of the 12 paths (`git grep` over the ledgers).
`metadata-forms-vocabulary.test.ts` asserts group keys only.
- **H3 held: both checks ran, and their hits are reported separately.**
Renamed key: 1 / 1 / 1 hit
(`dashboard.fields.refreshIntervalSeconds.label`), stale but not
contradicting. Penultimate edit: 18 / 18 / 22 flags, 0 contradicting.
## Verification (all at `aaee46778`)
- Build: `turbo run build --filter=@objectstack/cli...
--filter=@objectstack/platform-objects... --concurrency=2`: 59/59 tasks,
`VERDICT command-exit 0`.
- `pnpm --filter @objectstack/platform-objects test`: 58 files, 942
tests passed. `pnpm --filter @objectstack/platform-objects typecheck`:
exit 0, `check:test-typecheck: OK`.
- The new values ship: the built `dist/metadata-translations/index.mjs`
and `index.js` were imported, and `MetadataFormsTranslations` was read
by path after `withSourceFallback`. In both, 12 of 12 re-translated rows
equal the HEAD source and differ from the base, and 9 of 9
unchanged-leaf controls equal both.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands` (no paths)
derived 55 commands from the real diff, and all 55 exit 0. `--ran`
printed "55 derived famil(ies) accounted for — 55 run, 0 NOT-MEASURED (a
DERIVED zero — all 55 recorded an exit code and none of them is 3)".
`pnpm check:dual-build-cjs-loads` first refused with exit 3
(`PREREQUISITE NOT MET`: nine packages outside this diff had no
`dist/`). Those were built and the gate re-ran: exit 0.
- Named in the verdicts: `check:i18n` "OK (9 package(s) — all bundles in
sync, no undeclared authoring keys)"; `check:i18n-stale-fill` "OK (10
bundle set(s) — no new stale fills, 0 baselined)"; `check:nul-bytes`
exit 0.
- Lint, narrowed to the three edited bundles: `eslint --no-inline-config
--format json` read 3 files, 0 errors, 0 warnings.
`ESLint#isPathIgnored` answers `false` for all three, read from the
repo's own config. The config enables no type-aware linting (no
`parserOptions.project` or `projectService`, `eslint.config.mjs` states
it at line 328), so a change to string values in these files cannot move
the verdict on any other file. The full `pnpm lint` is left to CI.
- `origin/main` moved two commits past the base (`defc7f7b5`). Neither
touches a form declaration or a translation bundle, so the measurement
stands on it.
## Acceptance notes
- `dataset-panel-echo-decisions.test.ts`, the reason on the `include`
label row, cites "report.sections.joined_blocks.label is 关联对象" as a
precedent for the word 关联. After this PR that label reads joined 报表分块,
so the citation is out of date. It is prose: no assertion reads it, and
the `include` decision stands on its own, since 关联 keeps authored
precedents such as `sys_email.fields.related_object.label` 关联对象 in the
objects bundle. The ledger is outside this card's file surface and is
not edited here.
- zh-CN `page.sections.data_context.description` reads 关联对象与变量 ("related
objects and variables") against `en` "Record binding and page-local
state." It is loose but does not contradict: the bound record's object
is the related object, and the variables are the page-local state. No
instrument flags it, and it is left as written.
- The since-last-edit instrument cannot see a leaf that was wrong when
it was authored while `en` never moved. Instruments 5 and 6 cover only
the vocabularies they name. Instruments were run from the session
scratchpad.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 9b384f6 commit 5757463
4 files changed
Lines changed: 44 additions & 12 deletions
File tree
- .changeset
- packages/platform-objects/src/apps/translations
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
Lines changed: 4 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1853 | 1853 | | |
1854 | 1854 | | |
1855 | 1855 | | |
1856 | | - | |
| 1856 | + | |
1857 | 1857 | | |
1858 | 1858 | | |
1859 | 1859 | | |
| |||
2148 | 2148 | | |
2149 | 2149 | | |
2150 | 2150 | | |
2151 | | - | |
| 2151 | + | |
2152 | 2152 | | |
2153 | 2153 | | |
2154 | 2154 | | |
| |||
2248 | 2248 | | |
2249 | 2249 | | |
2250 | 2250 | | |
2251 | | - | |
| 2251 | + | |
2252 | 2252 | | |
2253 | 2253 | | |
2254 | 2254 | | |
| |||
2344 | 2344 | | |
2345 | 2345 | | |
2346 | 2346 | | |
2347 | | - | |
| 2347 | + | |
2348 | 2348 | | |
2349 | 2349 | | |
2350 | 2350 | | |
| |||
Lines changed: 4 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1853 | 1853 | | |
1854 | 1854 | | |
1855 | 1855 | | |
1856 | | - | |
| 1856 | + | |
1857 | 1857 | | |
1858 | 1858 | | |
1859 | 1859 | | |
| |||
2148 | 2148 | | |
2149 | 2149 | | |
2150 | 2150 | | |
2151 | | - | |
| 2151 | + | |
2152 | 2152 | | |
2153 | 2153 | | |
2154 | 2154 | | |
| |||
2248 | 2248 | | |
2249 | 2249 | | |
2250 | 2250 | | |
2251 | | - | |
| 2251 | + | |
2252 | 2252 | | |
2253 | 2253 | | |
2254 | 2254 | | |
| |||
2344 | 2344 | | |
2345 | 2345 | | |
2346 | 2346 | | |
2347 | | - | |
| 2347 | + | |
2348 | 2348 | | |
2349 | 2349 | | |
2350 | 2350 | | |
| |||
Lines changed: 4 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1798 | 1798 | | |
1799 | 1799 | | |
1800 | 1800 | | |
1801 | | - | |
| 1801 | + | |
1802 | 1802 | | |
1803 | 1803 | | |
1804 | 1804 | | |
| |||
1853 | 1853 | | |
1854 | 1854 | | |
1855 | 1855 | | |
1856 | | - | |
| 1856 | + | |
1857 | 1857 | | |
1858 | 1858 | | |
1859 | 1859 | | |
| |||
2248 | 2248 | | |
2249 | 2249 | | |
2250 | 2250 | | |
2251 | | - | |
| 2251 | + | |
2252 | 2252 | | |
2253 | 2253 | | |
2254 | 2254 | | |
| |||
2344 | 2344 | | |
2345 | 2345 | | |
2346 | 2346 | | |
2347 | | - | |
| 2347 | + | |
2348 | 2348 | | |
2349 | 2349 | | |
2350 | 2350 | | |
| |||
0 commit comments