Skip to content

fix(platform-objects): decide the report/dataset panel en-echoes per leaf, and pin the decisions as a ledger - #19401

Merged
huangyiirene merged 4 commits into
mainfrom
claude/issue-19355-repeater-row-property-en-echoes
Sep 20, 2026
Merged

huangyiirene merged 4 commits into
mainfrom
claude/issue-19355-repeater-row-property-en-echoes

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #19355

Clause-②: no

What this is

The card's deliverable is a per-leaf judgement with its reason recorded, not twelve translations. Every leaf below was decided on its own evidence; the verdicts and their reasons live in a ledger the suite reads (report-dataset-panel-echo-decisions.test.ts), not only in this body.

The reading, re-taken on this base

Re-derived mechanically on the worktree base 3ff6ddf1e, over the four keys the card names, in zh-CN / ja-JP / es-ES:

  • 12 label leaves byte-identical to their en source — the card's count is CONFIRMED, key for key and locale for locale.
  • 9 further helpText leaves at the same four keys were also byte copies (report.fields.dataset / values / rows, three locales each). They are the same authored unit as the label they sit on, so a fix that left them English would leave a panel whose field name is Chinese and whose tooltip is not.
  • dataset.fields.measures.helpText was already translated in all three locales. That leaf is this change's negative control and is recorded as such.

⇒ decided population: 4 keys × 8 string leaves × 3 locales = 24 decisions, of which 21 were echoes and 3 were already authored.

The instrument, with both controls

This package ships its own provenance table, PACKAGE/src/apps/translations/LOCALE.source-hashes.generated.ts: an entry exists exactly while that leaf is still a byte copy of the source revision.

  • positive control — every one of the 21 echoing leaves carried an entry, in all three locales;
  • negative control — dataset.fields.measures.helpText, the one leaf here a translator had already written, carried none in any of them.

After the translations, pnpm i18n:extract dropped exactly 21 rows (7 keys × 3 locales), 0 added. The table now records these leaves as authored rather than copied.

Per-leaf decisions

Every verdict below is translate — the echo was an unauthored extractor fill. That is a conclusion per leaf, not a policy: the ledger's echo verdict exists and costs one recorded sentence per locale, and no leaf here earned it.

leaf why it is a fill, not a rendering
report.fields.dataset (label) The same panel's blocks.dataset carries authored text in all three locales — 数据集 / データセット / Conjunto de datos — landed deliberately by #19345. Note this settles the one case triage flagged as possibly legitimate (Dataset in es-ES): this catalog's own authored answer for that word, in that locale, is "Conjunto de datos".
report.fields.dataset (helpText) Prose, not a term of art, and a byte copy in all three locales while the sibling helpTexts on the same panel (type, columns, order, chart) were authored.
report.fields.values (label) blocks.values on the same panel is authored 度量 / メジャー / Medidas.
report.fields.values (helpText) Prose. Its own sibling order.helpText names the same two vocabularies (rows/columns dimensions, values measures) in authored text in every locale — the vocabulary existed; this leaf never got it.
report.fields.rows (label) blocks.rows is authored 行 / 行 / Filas, and the adjacent top-level columns is authored 列 / 列 / Columnas on the same panel.
report.fields.rows (helpText) The neighbouring columns.helpText — the same sentence for the other axis — is authored in all three locales.
dataset.fields.measures (label) The sharpest case: the same leaf carried a hand-authored helpText in all three locales while its label was a byte copy. One leaf cannot be a decision and a fill at once.
dataset.fields.measures (helpText) Already authored before this card. No change; recorded because it is the negative control above.

The shared shape of the first seven: a translator who judged the English word right at the top level would not have written the translated one a few entries below it, in the same catalog, on the same panel, for the same concept.

The pin question, answered

The derived pin is not widened. A listed ledger is added instead.

repeater-row-properties.test.ts derives its population from the forms, and that derivation is the reason it is good: a repeater enumerated tomorrow is red the day it lands. Measured on this base:

  • its population is 124 enumerated repeater row properties, and 0 of them is an en-echo — these four keys are top-level form fields, outside it;
  • the only widening that would reach them is "all declared form-field paths", which is 417 paths — and that population is red on 230 further echo leaves across 82 keys today, none of them decided by this card.

A pin that lands red is not a pin, and widening it would also retro-scope #17508's carrier ruling. So the derived population stays as #17508 drew it, and the DECIDED leaves are enumerated in a new file whose header states the split. This is the ledger row the card asks for rather than prose: a future deliberate echo is a verdict: 'echo' row that must carry its own per-locale reason, and the suite refuses one without it.

Each row is also pinned to the en source it was decided against, so a reworded source reds the file instead of leaving a decision standing over text nobody judged.

Reverse verification

Both mutations ran through scripts/ablation-replace.mjs (anchor must hit; on-disk blob before/after; restore proven equal to the HEAD blob with git diff HEAD empty). Baseline first: 7 passed.

  • A — put one decided leaf back to its en source (zh-CN report.fields.values.label): blob 34c532953acf to 3f7269b0789b, run 1 failed / 6 passed, AssertionError: zh-CN report.fields.values.label reads its en source again. Restored, blob back to 34c532953acf.
  • B — reword the en source a ledger row is pinned to (Values to Value): blob b8a246889186 to 689bf01a679d, run 2 failed / 5 passed — the source pin AND the dark control (expected 7 to be 8), so the control is live too. Restored, blob back to b8a246889186.

An earlier attempt at A is reported as a no-op rather than quietly re-run: the mutation and its restore trap sat in one shell that exited between them, so the file was already restored when the suite ran and the green it printed measured nothing.

The generated-bundle hazard the claim flagged

These bundles are generated and git check-attr merge reads unspecified for all of them (control: packages/spec/spec-changes.json reads os-regen), so two branches adding i18n keys would merge clean and drop one side. Nothing collides here — this change adds and removes no keys at all, only leaf values — and the bundles were regenerated with the repo's own tooling, never by hand:

pnpm i18n:extract

It rewrote all eleven bundles; the three translated metadata-forms bundles round-tripped byte-identical to the hand-edited values, which is itself the evidence that the edits are exactly what the extractor's merge baseline produces.

Changeset, measured

@objectstack/platform-objects is not private, publishConfig.access is public, and files[] ships dist. Built, then grepped dist for a new string with a pre-existing translated leaf on the same panel as the positive control: both reach the same five published entry points (index.js, index.mjs, plugin.js, plugin.mjs, metadata-translations/index.*). A test-only string reaches none of them. ⇒ the diff publishes, skip-changeset would be wrong, and a patch changeset is included.

Verification

Head 68049082b.

  • pnpm --filter @objectstack/platform-objects test — 43 files, 599 tests passed (was 43/592 with the new file's 7 tests added; the translations directory alone goes 9 files/108 tests to 10/115).
  • pnpm --filter @objectstack/platform-objects typecheck — green, including check:test-typecheck. The new test file is in tsconfig.test.json's program (counted with --listFiles) and, by design, not in the main one.
  • Derived gate families: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack59 derived, 59 run, 0 NOT-MEASURED, every one recorded with its exit code. Includes check:i18n, check:i18n-stale-fill, check:nul-bytes, check:published-files, check:type-check-coverage, check:type-check-debt, check:cross-package-test-inputs, check:test-source-alias.
  • Two adjacent i18n gates not in the derivation, run anyway: check:i18n-coverage (13 configs, 621 baselined, none new) and check:i18n-walk-parity — both green.
  • pnpm lint — the whole repo, exit 0, no output.
  • One honest note on the sweep: check:dual-build-cjs-loads answered PREREQUISITE NOT MET (exit 3) on its first run because twelve packages had no dist yet. That is not a red and was not recorded as one; once the workspace was fully built it ran for real and passed (104 entry points across 67 packages).

Acceptance notes

Noted, not filed — observations, no defect named:

  • The dataset panel's own label, description and all four sections are still en echoes in all three locales, as are seven further top-level dataset.fields.* keys. Same class as this card, outside its four keys.
  • report.sections.dataset_binding (label and description) and report.fields.drilldown / runtimeFilter are echoes on the panel this change touches.

To file (reproducible defect, class (a)) — handed to the seat, not filed by this PR:

  • The metadata-form catalogs carry 82 further en-echo label keys (230 leaves) beyond the four Four repeater row-property labels ship byte-identical to their en source in all three locales (12 leaves) — pre-existing echo debt on the report and dataset panels #19355 decides. Measured on 3ff6ddf1e over all 417 declared form-field paths; 0 of them are inside repeater-row-properties.test.ts's 124-property population, so no pin sees them. A user in a translated locale reads English on those keys. It is the same class as this card and explicitly not mechanical — each key needs the same per-leaf judgement — so it is not taken in-place here. Dedupe words: en echo translated locale · metadata form field label untranslated · dataset.fields.dimensions object.fields.enable page.fields.interfaceConfig · declared form field echo census · translated leaf copy of source.

Generated by Claude Code

… card decided

Twelve label leaves and nine helpText leaves at the four keys #19355 names
shipped byte-identical to their `en` source in `zh-CN`, `ja-JP` and `es-ES`.
Each was judged per leaf; every one is an unauthored extractor fill, not a
rendering decision, and the evidence is per key in the PR body.

Claude-Session: https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE
Co-authored-by: Claude <noreply@anthropic.com>
…ables

`pnpm i18n:extract` — the command this package's extract config documents.
It rewrote all eleven bundles; the three translated `metadata-forms` bundles
round-tripped byte-identical, and the only change is the 21 provenance rows
for the leaves the previous commit translated, which the extractor drops
because those leaves are no longer byte copies of their `en` source.

Claude-Session: https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE
Co-authored-by: Claude <noreply@anthropic.com>
…s a ledger

One row per string leaf of the four keys #19355 names, each carrying the
verdict and the reason it was reached. The population is a list because the
neighbouring derived pin cannot take these keys: widening its 124 enumerated
repeater row properties to all 417 declared form-field paths lands red on 230
echo leaves across 82 keys nobody has decided yet.

Claude-Session: https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE
Co-authored-by: Claude <noreply@anthropic.com>
…ted panel leaves

Measured rather than assumed: the package is not `private`, `files[]` ships
`dist`, and the new strings reach five published entry points there
(`index.js`/`index.mjs`, `plugin.js`/`plugin.mjs`,
`metadata-translations/index.*`) — the same five a pre-existing translated
leaf on the same panel reaches. So the diff publishes and `skip-changeset`
would be wrong.

Claude-Session: https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Sep 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json fade3da23eb4c5ce580bac64e4495832e30bac8dpackageMentionDocs.

Which tree this was computed on

This run read content/docs from a90fa2f4277b9cd3427152f2c1f257b3d4b138b8 — the merge of head 68049082b9db68def033820be0b7887b373c8205 into base fade3da23eb4c5ce580bac64e4495832e30bac8d, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a90fa2f4277b9cd3427152f2c1f257b3d4b138b8 && git checkout a90fa2f4277b9cd3427152f2c1f257b3d4b138b8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin fade3da23eb4c5ce580bac64e4495832e30bac8d 68049082b9db68def033820be0b7887b373c8205 && git checkout -B drift-repro fade3da23eb4c5ce580bac64e4495832e30bac8d && git merge --no-ff 68049082b9db68def033820be0b7887b373c8205

node scripts/docs-audit/affected-docs.mjs --json fade3da23eb4c5ce580bac64e4495832e30bac8d

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

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 size/m tests tooling

Projects

None yet

2 participants