Filed by the domain:cli execution seat on the reserved judgement of the #14376 dev, which measured this class twice in one afternoon. ⛔ Ungraded and unrouted on purpose — no pm:*, no domain:*. It lands in scripts/check-i18n-coverage.mjs, which the lane table puts in domain:devx.
This is deliberately small. The dev that hit the problem argues explicitly against building anything larger, and its reasoning is carried below so the next reader does not re-propose the bigger options.
What happened, twice
PR #14651 (#14376) teaches the i18n extractor to walk three new families, one of them objects.OBJECT._validations.RULE.message. Its committed ratchet scripts/i18n-coverage-baseline.json therefore holds counts only that branch can compute — on main the walk does not exist, so other PRs move those counts without being able to see that they did.
Two PRs did exactly that while it waited to land:
| PR |
what it landed |
effect on the branch's baseline |
| #14517 |
a new authored rule message + four zh-CN rule translations in app-showcase |
454 → 450 |
| #14709 |
the remaining eight authored validation messages onto the translation channel |
450 → 443 |
Each cost a patch round: merge main, re-derive, rebuild every number in the PR body.
The property, stated generally
A PR that widens what a committed ratchet measures races every PR that changes what is measured, for its whole review-and-queue latency — and the race is invisible on main, because there the instrument does not exist.
Not i18n-specific. Type-check debt counts, token ratchets and liveness state counts have the same shape.
⭐ And the two collisions were not independent draws. Both are steps of one in-flight campaign moving the examples' authored validations[].message onto the #14253 channel. A campaign that systematically touches exactly the family a PR makes measurable collides on every step it lands. The forward-looking half is measurable and reassuring: app-showcase now has zero untranslated rule messages left, so that leg is finished; remaining exposure is app-crm (5) and app-todo (4) rule messages, plus bulkActions (18) and datasets (62) nobody is currently working.
⭐ It fails safe, and that is what caps the fix
The branch's own gate reds in both directions — the DOWN direction is what caught both collisions — and the merge queue rebuilds against current main. A stale number cannot land. The cost is latency and re-derive rounds, not correctness.
That is the argument for a one-line fix rather than a mechanism.
Proposed change
-
Name the class in scripts/check-i18n-coverage.mjs's own header. That file already carries this kind of institutional memory, so it is the natural home.
-
Add one sentence to the DOWN-direction remedy. Today it says only:
untranslated declared strings improved N -> M — ratchet DOWN: run node scripts/check-i18n-coverage.mjs --update and commit the baseline.
which reads as "your diff improved something" — when the usual cause is "main moved under you". Proposed addition, in the dev's words:
if you did not touch this population, merge origin/main first — the movement is probably not yours.
One line. It converts two confused rounds into a free hint for the next author.
⛔ What NOT to do — argued, not merely excluded
The dev names three larger options and argues against all of them:
- ⛔ Splitting the baseline per family. More files to go stale, same race.
- ⛔ Dropping the DOWN direction. That direction is what makes it a ratchet, and it is what caught both collisions here.
- ⛔ Computing the number instead of committing it. The committed number is what makes the change reviewable.
If a future reader wants one of these, the burden is to answer the fail-safe argument first: the race costs latency, not correctness.
Provenance
The seat holds no opinion on the grade; it does not own scripts/**.
Filed by the
domain:cliexecution seat on the reserved judgement of the #14376 dev, which measured this class twice in one afternoon. ⛔ Ungraded and unrouted on purpose — nopm:*, nodomain:*. It lands inscripts/check-i18n-coverage.mjs, which the lane table puts indomain:devx.This is deliberately small. The dev that hit the problem argues explicitly against building anything larger, and its reasoning is carried below so the next reader does not re-propose the bigger options.
What happened, twice
PR #14651 (#14376) teaches the i18n extractor to walk three new families, one of them
objects.OBJECT._validations.RULE.message. Its committed ratchetscripts/i18n-coverage-baseline.jsontherefore holds counts only that branch can compute — onmainthe walk does not exist, so other PRs move those counts without being able to see that they did.Two PRs did exactly that while it waited to land:
zh-CNrule translations inapp-showcaseEach cost a patch round: merge
main, re-derive, rebuild every number in the PR body.The property, stated generally
Not i18n-specific. Type-check debt counts, token ratchets and liveness state counts have the same shape.
⭐ And the two collisions were not independent draws. Both are steps of one in-flight campaign moving the examples' authored
validations[].messageonto the #14253 channel. A campaign that systematically touches exactly the family a PR makes measurable collides on every step it lands. The forward-looking half is measurable and reassuring:app-showcasenow has zero untranslated rule messages left, so that leg is finished; remaining exposure is app-crm (5) and app-todo (4) rule messages, plus bulkActions (18) and datasets (62) nobody is currently working.⭐ It fails safe, and that is what caps the fix
The branch's own gate reds in both directions — the DOWN direction is what caught both collisions — and the merge queue rebuilds against current
main. A stale number cannot land. The cost is latency and re-derive rounds, not correctness.That is the argument for a one-line fix rather than a mechanism.
Proposed change
Name the class in
scripts/check-i18n-coverage.mjs's own header. That file already carries this kind of institutional memory, so it is the natural home.Add one sentence to the DOWN-direction remedy. Today it says only:
which reads as "your diff improved something" — when the usual cause is "main moved under you". Proposed addition, in the dev's words:
One line. It converts two confused rounds into a free hint for the next author.
⛔ What NOT to do — argued, not merely excluded
The dev names three larger options and argues against all of them:
If a future reader wants one of these, the burden is to answer the fail-safe argument first: the race costs latency, not correctness.
Provenance
check:i18n-coverageis blind to them #14376 / PR fix(cli): walk bulkActions, validation messages and datasets in i18n extract, so the coverage ratchet can see them #14651 — where this was measured, twice. The accepting comment (5517086617) carries the full reasoning and the per-round numbers.check:i18n-coveragecounts one untranslated action key more than once — 70 of the 691 baselined units are byte-identical duplicate findings #14728 — a separate, unrelated defect in the same gate found in the same round: it counts one untranslated action key more than once (70 of 691 baselined units are byte-identical duplicates in the pre-existing_actionswalk). ⛔ Not this card.The seat holds no opinion on the grade; it does not own
scripts/**.