Skip to content

lint: an orphaned locale key is unfailable — translation-target-unknown is warning-only and its rule id carries no namespace, so no app can gate it #16310

Description

@claude

validate-translation-references reports an orphaned locale key correctly and
usefully — and no consumer can make that report fail anything. Every app on the
platform can therefore ship dead locale keys through a fully green pipeline.

This is not a request to build the rule. It exists and it works. The ask is
about severity and gateability.

Measured (hotcrm @ @objectstack/* 17.3.0)

Planting one orphan key in apps.crm_enterprise.navigation and one in
objects.crm_lead._sections, in all four locale bundles, then running the real
objectstack lint --json:

tree total warnings passed exit
baseline 13 1 true 0
8 orphan keys planted 21 9 true 0
restored 13 1 true 0

Each orphan is reported once per locale, with the id named and the remedy
printed. The diagnosis is excellent. The exit code is 0.

Why nothing downstream can gate it

Two independent properties, either of which alone would be enough:

  1. Severity is hard-coded warning. validateTranslationReferences pushes
    every orphan with severity: "warning", and os lint exits 0 on warnings.
    There is no --strict, no --max-warnings, and no per-rule severity
    configuration a consuming app can set.
  2. The rule id carries no namespace. Registry findings reach the JSON report
    as rule: f.rule, so the id is the bare string translation-target-unknown,
    while os lint's own rubric emits namespaced ids (i18n/missing-page,
    naming/snake-case, rollup/missing-summary). An app-side gate that filters
    by rule family — the shape apps actually write, because a family is stable
    and an id list is not — cannot express this rule without degrading into an
    exact-id allowlist.

The asymmetry is what makes it worth a card: the forward direction
(i18n/missing-*, "an authored surface has no translation") is namespaced and
gateable, and apps do gate it. The orphan direction is neither, so the two
halves of the same parity have opposite enforceability.

Consequence, in one sentence

A PR that deletes a navigation entry, a form section or a view and leaves its
locale keys behind is green everywhere, and the dead keys are actively
misleading afterwards: grepping the id returns a confident-looking hit in every
locale, which reads as "this exists and is translated".

Possible shapes, for the platform to choose between

  • A per-rule severity override consumers can set (the general fix; also closes
    the equivalent question for every other advisory rule).
  • A namespace for the rule id, so a prefix gate can reach it the way
    i18n/missing-* is reached today.
  • A --max-warnings style flag on os lint.
  • Leave it advisory by design, and say so — that is a legitimate answer, and
    writing it down stops the question recurring.

Back-link

Filed from hotcrm#1262, which measured this while deciding its own fix. HotCRM
closed its own case with a repo-side assertion over apps.*.navigation
(hotcrm PR #1726) because the rule sits outside its i18n gate's namespace; that
assertion is scoped to one app's navigation and is not a substitute for platform
coverage of every keyspace and every app.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions