Skip to content

finding(i18n): check-i18n-dead-keys reports LIVE keys as CONFIRMED when a consumer reads the pack by property access #6666

Description

@claude

Found while working the objectui#4730 dead-key batch (PR #6662). Filed unassigned as a sub-issue of that umbrella, because it is a defect in the umbrella's instrument rather than in its backlog of keys.

The defect

scripts/check-i18n-dead-keys.mjs reported five live keys at its strongest CONFIRMED tier. They were caught only by reading the consumer by hand; nothing in the tool's output hinted anything was missed.

Measured on 813bf8323:

  • console.loadingSteps.connecting
  • console.loadingSteps.loadingConfig
  • console.loadingSteps.preparingWorkspace
  • console.error.connectionFailed
  • console.error.checkServer

All five are read by packages/app-shell/src/chrome/LoadingScreen.tsx.

Why both legs of the evidence standard miss it

LoadingScreen.tsx is bootstrap-critical UI: it must render before i18n loads, which is exactly when the server is unreachable. Its own comment says so, and it deliberately does not call useObjectTranslation. Instead it imports the packs and reads them as plain object properties:

import{enasenLocale,builtInLocales}from'@object-ui/i18n';// ...conststrings=getStartupStrings();strings.loadingSteps.connecting// = console.loadingSteps.connectingstrings.error.connectionFailed// = console.error.connectionFailed
  • The AST pass classifies t() / tt() call sites. There is no call, so nothing is visited.
  • The text safety net greps the full dotted key. The full dotted key is never spelled: the namespace segment is bound to a local variable, so the source contains strings.loadingSteps.connecting and never console.loadingSteps.connecting.

So the key is invisible to both, and lands in CONFIRMED — the tier documented as "no call site and no textual occurrence anywhere else in the repo", which reads as the safest thing to delete. Deleting this family ships a blank splash screen in ten locales on precisely the server-down boot the screen exists to explain.

The blind spot is a class, not these five keys

Any consumer that reaches a pack through a reference whose variable name replaces the namespace prefix is invisible the same way. Today the pack-object importers outside packages/i18n are:

  • packages/app-shell/src/chrome/LoadingScreen.tsx (the case above)
  • packages/app-shell/src/console/ai/outboundAgentText.ts — indexes console.ai dynamically (ai?.[key]). Its four keys happen to land in NEEDS-REVIEW because the property names appear as literals in a nearby type union, so the net catches them by luck, not by design.
  • packages/plugin-grid/demo/main.tsx and demo/bulk-actions.tsx — whole-pack resources only, no per-key reads.

The set is small today, which is what makes this cheap to close now.

Suggested remedy

Add a property-chain leg to the text safety net: alongside the full dotted key, probe the tail chain (.loadingSteps.connecting). Validated on this batch with both controls before being trusted:

  • the five live keys above: detected (1 hit each);
  • console.error.serverUnreachable and console.error.timeout, which genuinely have no pack reader: 0 hits.

So the leg discriminates rather than demoting everything. Note it is only meaningful for keys of three or more segments — a two-segment key yields a one-word tail (.no, .ok, .empty) that is far too generic. For two-segment keys the sound check is the enumerated importer set above, which is what PR #6662 used.

A cheaper partial mitigation, already in place: PR #6662's retirement pin names the nine bootstrap strings, which gives them a textual footprint and demotes them from CONFIRMED to NEEDS-REVIEW in the tool's own output. That protects this one family only, and by side effect rather than by design.

Why this is filed separately from the umbrella

objectui#4730 tracks keys; this tracks the tool every future round of it depends on. The umbrella's remaining trims (search, table, report, form, appDesigner) will each be triaged against this same CONFIRMED tier, so the false-positive class is live work, not history.

Back-links: objectui#4658 (the sweep mechanism and its three-legged evidence standard), objectui#4730 (the backlog), PR #6662 (where this was measured).

Generated by Claude Code


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopm:queuepriority:p1

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions