Skip to content

Commit 8450eea

Browse files
yinlianghuiclaude
andauthored
feat(gate): detect translation leaves stranded on a previous source revision (#12067)
* feat(gate): detect translation leaves stranded on a previous source revision `os i18n extract --fill=default` fills gaps only, so the ordinary sequence — extract, revise the source string, extract again — rewrites `en` (never merged, #8543) and silently keeps the previous source text in every translated locale. The bundle stays in sync BY KEY, so `check:i18n` reports OK, and the leaf is still present, so `check:i18n-coverage` counts it translated: it tests presence, not freshness. Measured on PR #11659 at bbe0b17 — three locales serving a 602-char superseded draft of a 411-char help string under 31 green checks. "Flag untranslated leaves that no longer match the current source" cannot be implemented as stated: "untranslated" has exactly one observable spelling today, EQUAL to the current source, so the two predicates are complementary rather than conjoinable. Measured on this tree, 2648 of 3010 leaves differ from `en` — that set is essentially every correct translation in the repo. What IS decidable without provenance is cross-locale agreement: two different target languages do not independently produce byte-identical prose, so when they hold the same bytes neither translated it — both were filled from the source, and if those bytes are not the CURRENT source they are a previous revision of it. Three further conditions each close one measured false-positive class (47 -> 11 -> 5); none of them is a threshold. Existing drift is frozen with a reason per entry, all five of it real and pre-existing; NEW drift fails. Repairing those bundles is deliberately not this change — #11671 was re-routed to the tooling lane for that reason. Needs no workspace build (it reads the committed bundles as text), so it runs in `Lint & Repo Gates` rather than with its two i18n siblings in `typecheck`. Part of #11671 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6 * fix(gate): put check-i18n-stale-fill's dispatch behind isEntrypoint The module exports its rules so they can be imported; without the guard that import runs the whole gate inside the importer and exits its process — the silent-success direction scripts/invoked-as.mjs documents. check:entry-guard caught it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6 * chore(pm): record check:i18n-stale-fill's bare-root verdict as REFUSE-UNSPELLABLE The new gate reaches its population through the same findExtractConfigs walk check:i18n and check:i18n-coverage use, so it joins them rather than being judged apart: the walk recurses across the root, but what it ADMITS is a filename-and-segment test — 9 of 5185 tracked files (0.17%). Measured rather than inherited: both star spellings of the real population collapse to the same malformed double-separator prefix, and hintCovers matched it against NOTHING when checked against a real config path; the only spellings that cover anything collapse to the bare root. So a declaration is either a live hint over zero files or a hint over 5185 files to reach 9 — and declaring a root the gate does not read wholesale is the costlier error. Only the TRIAGE map moves. The recogniser, the sweep and the self-test are untouched. Part of #11671 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 40225e9 commit 8450eea

6 files changed

Lines changed: 541 additions & 1 deletion

File tree

.github/workflows/lint.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,19 @@ jobs:
13881388
- name: Reserved-word ("role") docs ratchet
13891389
run: pnpm check:role-word
13901390

1391+
# #11671: `os i18n extract --fill=default` fills GAPS only, so revising a
1392+
# source string rewrites `en` and strands the previous source text in every
1393+
# other locale. The bundle stays in sync BY KEY, so `check:i18n` reports OK
1394+
# and `check:i18n-coverage` counts the leaf translated (it tests presence,
1395+
# not freshness) — measured on PR #11659, three locales serving a 602-char
1396+
# superseded draft of a 411-char help string under 31 green checks.
1397+
# This gate reads the committed bundles as TEXT — no workspace build — which
1398+
# is why it sits here rather than with its two i18n siblings in `typecheck`.
1399+
# Existing drift is frozen in scripts/i18n-stale-fill-baseline.json with a
1400+
# reason per entry; NEW drift fails. Improvements ratchet down via --update.
1401+
- name: Stale translation fills (i18n) ratchet
1402+
run: pnpm check:i18n-stale-fill
1403+
13911404
# #6319: content/docs/getting-started/quick-reference.mdx is the protocol
13921405
# index, and each "## <Name> Protocol (N schemas)" heading is a DECLARATION
13931406
# about the table under it. The page is hand-written (build-docs.ts writes

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"i18n:extract": "tsx packages/cli/bin/run-dev.js i18n extract packages/platform-objects/scripts/i18n-extract.config.ts --locales=zh-CN,ja-JP,es-ES --fill=default --out=packages/platform-objects/src/apps/translations",
3333
"check:i18n": "node scripts/check-i18n-bundles.mjs --self-test && node scripts/check-i18n-bundles.mjs",
3434
"check:i18n-coverage": "node scripts/check-i18n-coverage.mjs --self-test && node scripts/check-i18n-coverage.mjs",
35+
"check:i18n-stale-fill": "node scripts/check-i18n-stale-fill.mjs --self-test && node scripts/check-i18n-stale-fill.mjs",
3536
"check:app-nav-i18n": "pnpm --filter @objectstack/cli run check:app-nav-i18n",
3637
"check:nul-bytes": "node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs",
3738
"check:entry-guard": "node scripts/check-entry-guard.mjs --self-test && node scripts/check-entry-guard.mjs",

0 commit comments

Comments
 (0)