Skip to content

[finding] check:i18n verifies key presence, not that an untranslated leaf still matches the source string it was filled from — and the drift is sticky #11671

Description

@os-sam

Measured on PR #11659 (card #11507) by the domain:services seat and its dev. Filed unassigned, no pm:queue — grading is triage's field. ⚠️ Routing is a guess: the gate is repo tooling (check:i18n / the bundle extract config), so if this belongs to a tooling lane rather than services, please re-route rather than treating the label as a claim.

The gap

check:i18n checks that bundles are in sync by key — every expected key present, no undeclared authoring keys. It does not check that a leaf which was filled with the source string still equals that source string after the source is revised.

So a locale bundle can publish a superseded draft of a description indefinitely, and the gate reports OK.

Measured instance

On PR #11659 at head bbe0b17, sys_activity.fields.type.help:

localelengthstate
en411 charsmatches the source description
es-ES602 charssuperseded draft
ja-JP602 charssuperseded draft
zh-CN602 charssuperseded draft

The three stale leaves carried two clauses the declaration no longer made — including an implementation detail (about readonly fields and validateRecord) that had been deliberately removed from the shipped help text.

At that same commit the gate reported, verbatim:

check-i18n-bundles: OK (1 package(s) — all bundles in sync, no undeclared authoring keys)

⭐ And it was not only the gate. The full CI suite was green at bbe0b17 — 31 checks, 29 success / 2 skipped / 0 failures, Lint & Repo Gates included — while that drift was live. Nothing in the repo's automated defences can currently see it.

Why it is worth a card: the drift is sticky

--fill=default fills gaps only. Once a non-en leaf holds any non-empty value, a later regeneration leaves it alone. So this does not self-heal: it persists until someone deliberately drops the leaf and re-fills it, which is exactly what the repair on #11659 had to do.

The mechanism that produces it is ordinary, not exotic: extract once → revise the source string → extract again. The second run rewrites en (the source locale) and silently keeps the stale copies everywhere else.

Class, not instance

Any package whose bundles are filled with source text and whose source string is later revised. plugin-audit is where it was caught; nothing about the mechanism is specific to it.

What was NOT done, deliberately

⛔ The PR was not widened to fix the gate — the repair on #11659 was limited to regenerating the four affected leaves (they are now byte-equal to the source, 411 chars each, verified independently by the seat with git show <sha>:<path> and a sha256 comparison carrying a passing negative control). Whether the gate should grow this check, and at what cost, is a separate decision.

Not measured / not proposed here: what the fix would cost, whether a source-hash-per-leaf is the right shape, and whether check:i18n-coverage (not run on that PR — it needs a full workspace build) already covers part of this. Those belong with whoever takes the card.

Refs: #11507 (the card this surfaced under) · PR #11659

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions