Skip to content

Commit 7e8b09c

Browse files
committed
tooling(i18n): ledger the three unwalked translation groups by maintainer ruling
The walk-parity gate's own first red named three declared translation groups with no extractor face: `messages`, `settingsCommon` and `settings`. The maintainer ruled (2026-09-04, #14653 comment 5535827386) that all three enter the gate's exemption ledger in this PR, each with its reason, and that no `settings` emitter is built now. That act is ⛔ MAINTAINER-ONLY by the gate's own failure text, so it is taken under that ruling and nowhere else: messages keyed by arbitrary ids composed at the `i18n.t()` call sites; no registry or stack config enumerates the id set. settingsCommon the Settings UI's own five source-badge labels — the console's words in every app, ruled out of per-app bundles on #7646. settings keyed by `SettingsManifest.namespace`, which is platform code rather than authored metadata. A DEFERRAL, not a fact: the terminal state is held on #15178. `LEDGER_CEILING` moves 0 -> 3 in the same diff, which is the mechanism working as designed. No check is weakened: the reason floor, the placeholder refusal, the both-direction size ratchet, the stale-entry detection, the empty-population refusal and the MAINTAINER-ONLY remedy text are all untouched. The self-test's `ledger is empty` pin is replaced by three that stay true and still bite — the ledger's key set equals the recorded unwalked set, every reason survives the gate's own reason checks, and its size is exactly the ceiling under the real ratchet classifier, so an entry without a ceiling bump (or a ceiling bump without an entry) reddens in the self-test as well as in production. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WXyGTWPbbreqXow7Z2pZCk
1 parent 2e33c87 commit 7e8b09c

1 file changed

Lines changed: 57 additions & 11 deletions

File tree

scripts/check-i18n-walk-parity.mjs

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@
5555
* over one fixture that authors a member of every group. The
5656
* walker's own output, not a transcription of its header.
5757
* ledger the groups that legitimately have no extractor face at all, each
58-
* carrying the REASON. Starts EMPTY and only shrinks (below).
58+
* carrying the REASON. Holds three entries by the 2026-09-04
59+
* ruling, and only shrinks from there (below).
5960
*
6061
* and the assertion is two set differences:
6162
*
@@ -171,22 +172,51 @@ const DECLARED_WATCH_HINTS = [
171172
// ── The exemption ledger ────────────────────────────────────────────────────
172173

173174
/**
174-
* ⛔ SHRINK-ONLY, and it starts EMPTY. Groups that legitimately have no
175-
* extractor face, keyed by group name, valued by the REASON.
175+
* ⛔ SHRINK-ONLY, and every line in it was RULED IN — never added to reach
176+
* green. Groups that legitimately have no extractor face, keyed by group name,
177+
* valued by the REASON.
178+
*
179+
* It no longer starts empty. On this gate's own first red the maintainer ruled
180+
* (#14653, 2026-09-04, comment 5535827386) that all three unwalked groups enter
181+
* here with their reasons, and `LEDGER_CEILING` moved 0 → 3 in that same diff.
182+
* Each entry below is that decision written down, with the reason that carries
183+
* it — the ⛔ MAINTAINER-ONLY act the unwalked-group message names, performed
184+
* by the owner it names rather than by a landing author getting past a check.
185+
*
186+
* `settings` is the one DEFERRAL among the three, and it says so out loud so
187+
* that nobody reads the exemption as the answer: its terminal state — a
188+
* registry-driven emitter on the `metadataForms` precedent, or removal from the
189+
* per-app schema — is held on #15178.
176190
*
177191
* Read the class-level note above before adding anything here. The one-line
178-
* version: a red belongs to whoever owns the walker, and an entry here is a
179-
* decision, not a way past one.
192+
* version: a red belongs to whoever owns the walker, an entry here is a
193+
* decision and not a way past one, and growth stays MAINTAINER-ONLY — from here
194+
* the ledger only ever shrinks.
180195
*/
181-
const KNOWN_NO_EXTRACTOR_FACE = Object.freeze({});
196+
const KNOWN_NO_EXTRACTOR_FACE = Object.freeze({
197+
messages:
198+
'Keyed by arbitrary ids composed at the `i18n.t()` call sites (plugin-audit\'s activity-feed and '
199+
+ 'mention strings); no registry and no stack config enumerates that id set, so there is no face for '
200+
+ 'an extractor to walk.',
201+
settingsCommon:
202+
'The Settings UI\'s own five source-badge labels (env / global / tenant / user / default) — the '
203+
+ 'console\'s words in every app rather than any app\'s own, ruled out of the per-app bundles on '
204+
+ '#7646.',
205+
settings:
206+
'Keyed by `SettingsManifest.namespace`, and manifests are platform code '
207+
+ '(`packages/services/service-settings/src/manifests/*.manifest.ts`), not authored metadata: no stack '
208+
+ 'config carries them, and no consumer asks for per-app settings translations today. A DEFERRAL, not '
209+
+ 'a fact — the terminal state (a registry-driven emitter on the `metadataForms` precedent, or removal '
210+
+ 'from the per-app schema) is held on #15178.',
211+
});
182212

183213
/**
184214
* The ledger's size ceiling. Growth requires editing this number in the same
185215
* diff as the entry, which is the whole mechanism: it makes growth a reviewed
186216
* act rather than a line nobody re-reads. Shrink-only in both directions — a
187217
* ceiling above the real size fails as slack, so it can only be walked down.
188218
*/
189-
const LEDGER_CEILING = 0;
219+
const LEDGER_CEILING = 3;
190220

191221
/**
192222
* A reason must be a real sentence. The floor is 24 characters and a
@@ -418,7 +448,7 @@ function list(declared, walked, ledger) {
418448
if (extra.length) console.log(` ⚠️ emitted but NOT declared: ${extra.join(', ')}`);
419449
console.log(`\n ledger (${ledgerKeys.size}/${LEDGER_CEILING})`);
420450
for (const [g, reason] of Object.entries(ledger)) console.log(` ${g}: ${reason}`);
421-
if (!ledgerKeys.size) console.log(' (empty — as it shipped)');
451+
if (!ledgerKeys.size) console.log(' (empty — fully paid down)');
422452
return 0;
423453
}
424454

@@ -485,7 +515,10 @@ const RECORDED_DECLARED = [
485515
const RECORDED_WALKED = [
486516
'apps', 'dashboards', 'datasets', 'flows', 'globalActions', 'metadataForms', 'objects', 'pages',
487517
];
488-
/** …and the verdict those two produce against an EMPTY ledger. */
518+
/**
519+
* …and the verdict those two produce against an EMPTY ledger — which, since the
520+
* 2026-09-04 ruling, is also exactly the shipped ledger's key set.
521+
*/
489522
const RECORDED_UNWALKED = ['messages', 'settings', 'settingsCommon'];
490523

491524
let selfTestReachedVerdict = false;
@@ -548,7 +581,18 @@ function selfTest() {
548581

549582
// 13 — the recorded sample: the classifier's verdict over today's REAL names.
550583
eq('recorded sample', parityVerdict({ declared: RECORDED_DECLARED, walked: RECORDED_WALKED, ledger: {} }).unwalked, RECORDED_UNWALKED);
551-
eq('recorded sample: ledger is empty', Object.keys(KNOWN_NO_EXTRACTOR_FACE).length, 0);
584+
// 14 — the SHIPPED ledger, pinned three ways so the ruling that filled it
585+
// cannot erode into an allowlist: it names exactly the recorded unwalked
586+
// set (nothing more, nothing less), every reason survives the gate's own
587+
// reason checks, and its size is the ceiling — the ratchet classifier, so
588+
// an entry added without the ceiling, or a ceiling raised without the
589+
// entry, reddens here as well as in production.
590+
eq('shipped ledger: keys are exactly the recorded unwalked set',
591+
Object.keys(KNOWN_NO_EXTRACTOR_FACE).sort(), RECORDED_UNWALKED);
592+
eq('shipped ledger: every reason passes the reason checks',
593+
ledgerShapeProblems(KNOWN_NO_EXTRACTOR_FACE).length, 0);
594+
eq('shipped ledger: size is exactly LEDGER_CEILING',
595+
ledgerRatchetProblems(KNOWN_NO_EXTRACTOR_FACE, LEDGER_CEILING).length, 0);
552596
eq('recorded sample: hints are live', DECLARED_WATCH_HINTS.length > 0, true);
553597

554598
if (failures.length) {
@@ -563,7 +607,9 @@ function selfTest() {
563607
`✓ check-i18n-walk-parity self-test: ${cases} cases pass — an added declared group and a deleted `
564608
+ 'emitter are both named, a ledgered group is not, a blank/placeholder/short reason is refused, '
565609
+ 'a stale entry (undeclared, or now walked) fails, the size ratchet refuses growth AND slack, '
566-
+ 'an empty side is refused, and the recorded sample of today\'s real group names reproduces its verdict.',
610+
+ 'an empty side is refused, the recorded sample of today\'s real group names reproduces its verdict, '
611+
+ 'and the SHIPPED ledger is pinned to exactly that recorded unwalked set — every reason passing the '
612+
+ 'reason checks, its size exactly at the ceiling.',
567613
);
568614
selfTestReachedVerdict = true;
569615
return 0;

0 commit comments

Comments
 (0)