Uh oh!
There was an error while loading. Please reload this page.
Hold every inline defaultValue to the placeholder spelling the provider-less fallback resolves - #6218
Conversation
… fallback resolves `check:i18n-keys`' `default-value-drift` class pins an inline default byte-identical to its `en` row, and objectui#3512 holds `en` to the one placeholder spelling `createSafeTranslation`'s `fallbackT` interpolates — so most inline defaults are covered transitively. Re-measured on this tree, 66 of 1003 are not: 3 literals on dynamic keys, 63 written as a computed expression. Class 7, `unresolvable-default-spelling`, puts #3512's rule beside `holesOf()` and applies it to the text an inline default carries — the folded sentence, or a template literal's static segments. Judged over every inline default (977) rather than only the residue (37 with readable text, 24 with none), so the count is a live control instead of a set that could silently empty; the CLI exits non-zero below 500. Single-brace holes and JSX braces are out of range by construction: the rule is handed a literal's TEXT, never source. Closing the residue at the source reaches only 5 sites, and the measurement behind that is the load-bearing part. react-i18next's not-ready `t` returns `options.defaultValue` VERBATIM — it does not interpolate. So at a call site bound to a bare `useObjectTranslation()`, `` `Signed in as ${user.email}` `` is the only form that renders correctly with no provider, and rewriting it to `'Signed in as {{email}}'` would put literal braces in front of the user — the exact defect this family of cards exists to prevent. `objectBulkActionDispatch.test.tsx` fails on precisely that substitution. The 5 that are safe: four whose default carries no hole (the record-form submit button, which also stops falling back to `Save` where the pack renders `Update`; the context-selector package label; the approvals separator) and one behind a `createSafeTranslation` hook, whose fallback does interpolate. Part of #4905 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
yinlianghui-tw
commented
Aug 25, 2026
PM review — ACCEPT. ⭐ Route C was my order's, and applying it as written would have shipped the defect this card family exists to prevent.Reviewed by the The finding that reverses the card, triage, and my order togetherAll three of us assumed the 63 computed defaults were unpinned near-misses that route C would sweep up. They are mostly the correct form already. ⛔ So route C, applied as I specified it, buys coverage by introducing the defect. My order's target of 971/971 was not a stretch goal that fell short — it was unreachable in a direction that would have made things worse. And the way you established it is the part I want on the record. You applied C to all 39 comparable sites first,
B as the primary deliverable is the right recoveryClass 7 The premise re-derivation is also a correction and reported as one: 66 of 1003, not the card's 65 of 971. Non-vacuity — four directions, and the third is the one that matters
⭐ Leg 2 is unusually careful. Showing that the old gate also reds on a newly-comparable site is what distinguishes "C made this reachable by the existing pin" from "the new rule is quietly doing the old rule's job" — two stories with the same green, told apart on purpose. Every mutation proven on disk by anchored counts, restored via The exclusions are individually argued, and one is flagged against itselfThe 29 reverted sites are enumerated by file with the shared cause. The four And
|
Uh oh!
There was an error while loading. Please reload this page.
Part of #4905
What this closes
check:i18n-keys'default-value-driftclass pins an inlinedefaultValuebyte-identical to itsenrow, and objectui#3512 holdsento the one placeholder spellingcreateSafeTranslation'sfallbackTinterpolates — so most inline defaults are covered transitively. Re-derived on this tree (the card's census is from 2026-08-21 and has moved):Route B is the primary deliverable; route C turned out to reach only 5 sites, and why is the load-bearing result below. A was not attempted.
The measurement that inverted route C
The card, the triage note and the dispatch all assumed the 63 computed defaults were unpinned near-misses —
`Signed in as ${user.email}`where the pack saysSigned in as {{email}}— and that rewriting them to theenvalue would be behaviour-preserving. I applied that rewrite to all 39 comparable sites, andpackages/plugin-grid/src/__tests__/objectBulkActionDispatch.test.tsxwent red on three cases. Bisected to one line, then measured the cause inreact-i18next/dist/es/useTranslation.js:react-i18next's not-ready
treturnsdefaultValueverbatim — it does not interpolate. Three things can render an inline default, and only two of them interpolate it:createSafeTranslation'sfallbackT{{k}}needlenotReadyTuseObjectTranslation()with no i18next instanceSo at a call site bound to a bare
useObjectTranslation(), the template literal is the only one of the two forms that renders correctly with no provider. "Making it comparable" would have put literal{{braces}}in front of a user on exactly the provider-less host this family of cards is about — a coverage number bought by introducing the defect. Per the dispatch's "do not weaken the pin to make sites fit", those sites stay computed and are covered by the spelling class instead.Also left alone, for a separate measured reason: the five
PermissionFacetLinksites. Four of their keys are i18next plural families (perm.facet.objects+perm.facet.objects_one), so the drift rule'senValueis the family's base form; byte-equality against it would delete the English singular thatperm-home-namespace-3546.test.tsxalready pins count by count in all ten languages — a strictly stronger assertion than the one it would have been traded for. The fifth (perm.facet.more) is flat and safe, but its source spelling is the declared premise of that same equivalence case, and it is already covered there.The 5 that are safe, and why each one is
Four carry no placeholder at all, so all three renderers agree; one sits behind a
createSafeTranslationhook, which does interpolate.AppContent.tsxsubmit button ×2t('common.save', …)/t('common.create', …)→'Update'/'Create'Savewhere the pack rendersUpdateContextSelectors.tsxrawLabel→'Package'rawLabel === 'Package'guard two lines upapproverIdentity.tsDEFAULT_NAME_SEPARATOR→', 'DetailSection.tsx'Show {{count}} empty fields'useDetailTranslationis acreateSafeTranslationfactory, sofallbackTinterpolates itRoute B — class 7,
unresolvable-default-spellingobjectui#3512's rule, placed beside
holesOf()where the call-site classification already lives, applied to the text an inline default carries: the folded sentence, or a template literal's static segments (the surface class 3 structurally cannot judge).{{ name }},{{count, number}},{{- name}}and$t(key)are refused wherever they are written.Judged over every inline default (977), not only the residue — a pinned default is byte-equal to an
envalue #3512 already covers, so those come back green twice over, which is what makes the count a live control instead of a rule whose whole subject is a handful of strings that could silently become zero. Single-brace{x}holes (objectui#4135) and JSXstyle={{…}}braces are out of range by construction: the rule is handed a literal's text, never source.Non-vacuity — each direction predicted before it was run
ReportConfigPanel.tsx:198,'Save'→'Save changes'). Staged a copy of the pre-change gate and ran both. Verdicts byte-identical, bothexit 1:DeviceAuthPage.tsx:314is one of the 5 → mutated'Signed in as {{email}}'→'{{ email }}'. Predicted both classes; got both —default-value-driftandunresolvable-default-spelling,exit 1. The pre-change gate also reds on it now, which is the point: route C moved it under the existing pin rather than under the new rule.HomePage.tsx:415(dynamic key, so no transitive pin can ever reach it) →'Welcome'→'Welcome {{ name }}'. Pre-change gate:exit 0, silent. New gate:exit 1, naming the site. This is B's non-vacuity proof.Every mutation was proven on disk by anchored counts (anchor
1→0, injection0→1) — never an editor's exit code — and restored withgit checkout HEAD --plus the explicit path, proven by an emptygit diff HEAD.Verification, at
50d30a8c4(the final commit)pnpm lint— 47/47 tasks successful, 0 errors (2673 pre-existing warnings). Full repo, not narrowed.check:i18n-keysexit 0·check:i18n-driftexit 0— "0 en value(s) changed", no pack was touched ·check:control-bytesexit 0·check:phantom-depsexit 0·check:i18n-dead-keysexit 0(report-only; candidate count unchanged at 389)changeset:checkexit 0·type-check:scriptsexit 0packages/plugin-detail/+packages/plugin-grid/— 191 files, 1899 tests passed; the 36 app-shell test files reading the changed modules — 319 tests passed.Exit codes captured by redirect before any pipe; gate results quoted from each gate's own verdict line.
scripts/i18n-call-site-key-baseline.jsonis not touched.One test assertion narrowed
scripts/__tests__/check-i18n-call-site-keys.test.tspinned"t?.('common.package', { defaultValue: rawLabel }) ?? rawLabel"as one string. That case is about thedead-sibling-fallbackrule abstaining on an optional call with a live??operand — the options object between them is a different rule's territory. Narrowed to the optional call and the??operand, so a class-3 fix stops reading as a class-5 regression.Generated by Claude Code