Repository navigation
docs(i18n): state the shipped severity for an unresolvable translation key - #18766
Merged
os-try-charles merged 1 commit intoSep 17, 2026
Merged
Conversation
…n key `content/docs/protocol/kernel/i18n-standard.mdx` promised that an unresolvable translation-bundle key "is a **warning**, not an error: nothing crashes". Both halves are false against the shipped linter: `translation-target-unknown` has carried `severity: 'error'` since #16310, so `os validate`, `os lint` and `os compile` each exit non-zero on one. An author who read the page and then wrote a locale key against a target the linter cannot resolve got a failed run, having been told in writing it would be a nag. The whole claim is repaired, not just the severity word: - the severity now reads `error`, names the rule id so it can be grepped, and names the three commands that exit non-zero on it; - the runtime half ("nothing crashes ... renders in its source locale") is kept, because it is still true and is precisely the reason the rule gates: the orphan key stays a confident-looking grep hit for a surface that was deleted; - the sibling rule is separated out, since the very next paragraph is about it: `translation-option-key-unknown` is still a warning. The linter is untouched by design. #16310 was a deliberate promotion, so the page is what drifted. Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk Co-authored-by: Claude <noreply@anthropic.com>
os-try-charles
marked this pull request as ready for review
September 17, 2026 20:03
os-try-charles
deleted the
claude/issue-18452-i18n-unresolvable-key-severity
branch
September 17, 2026 20:21
This was referenced Sep 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #18452
content/docs/protocol/kernel/i18n-standard.mdxtold authors that an unresolvabletranslation-bundle key "is a warning, not an error: nothing crashes". Both halves are
false against the shipped linter, and have been since #16310 promoted
translation-target-unknowntoerror. An author who read the page and then wrote a localekey against a target the linter cannot resolve got a FAILED run, having been told in writing
it would be a nag.
The two texts, re-measured on this branch
Repository
objectstack-ai/objectstack, worktree at basef1c9bb305.content/docs/protocol/kernel/i18n-standard.mdx:1027(before this PR)An unresolvable key is a **warning**, not an error: nothing crashes, the stringpackages/lint/src/validate-translation-references.ts:151const TRANSLATION_TARGET_UNKNOWN_SEVERITY = 'error' as const;severityof every finding at:912and:1364— the card's:813/:1265are stale, so everything here was located by CONTENTtranslationmatches 49 lines in that same mdxWhat an
erroractually does, read from the consuming commands rather than inferred from theseverity word:
os lint—failing = errors.length + (strict ? warnings.length : 0)andif (failing > 0) process.exit(1)(packages/cli/src/commands/lint.ts:918-922,:1040).No flag is needed;
--strictonly adds warnings on top.os validate—splitBySeverity(findings), thenthis.exit(1)on a non-emptyruleErrors(
packages/cli/src/commands/validate.ts:353-383).os compile— the same shape (packages/cli/src/commands/compile.ts:370-397).The rule reaches all three because its suite entry declares
commands: ALL(
packages/lint/src/authoring-rules.ts:709-713).Where the repair's boundary was drawn
The old sentence promised two things — "a warning, not an error" AND "nothing crashes" — so
repairing only the severity word would have left a worse half-truth than the original. The whole
claim is repaired instead:
three commands that exit non-zero;
promotion: the string really does render in its source locale while its neighbours resolve, and
that invisibility is what makes an orphan key worth failing on. The rule's own module header
makes the identical argument (
packages/lint/src/validate-translation-references.ts:29-48);translation-option-key-unknownis still awarning(
packages/lint/src/validate-translation-references.ts:1309,:1328).Out of bounds on purpose: the two places on this page that describe the key shape (the card
measured both as correct) are untouched, as is
content/docs/ui/translations.mdx.packages/lint/**is untouched by design — #16310 was a deliberate promotion, so the page iswhat drifted, and downgrading the rule would be the gate-weakening the triage ruling forbids.
The sibling sweep — the spellings and the populations, named
The card's starting point ("under
content/docs, exactly one file mentionsunresolvable keyortranslation-target-unknown") is a reading over ONE spelling, not over the claim, so it wasre-run wide. Probes, all on
f1c9bb305, all case-insensitive:translation-target-unknownunresolvableorphan[- ](key|translation)/orphaned (key|translation)nothing crasheswarning,? not an error/not an error,? (a|just a) warninginertcontent/docs,skills,examples,apps,README.mdwarning|warn|error|severity|fails the run|fails the build|exit 1) on the same line as translation-key language (translat|locale|i18n|bundle key|orphan)content/docs,skills,examples,apps,README.md, minuscontent/docs/referencesandcontent/docs/releasesadvisory|does not fail|doesn't fail|won't fail|never fails|non-blocking|non-gating|safe to ignorenear translation languagecontent/docs,skills,examples,docs,appsrenders? (in its )?(source|original) locale/renders? untranslated/stays englishcontent/docs,skills,examples,docs,appsdocs,.claude,apps,skills,contenti18n-standard.mdxitselfWhat they returned:
content/docs— the sentence this PR repairs. Probe 11 confirms the pagestates the severity claim exactly once; the other hits on that page are
Callout type="warn"markers and an unrelated parse-failure sentence.
skills/objectstack-i18n/SKILL.md:196-197already states the claim correctly: "
translation-target-unknownis an error and failsthe run;
translation-option-key-unknownis a warning." The probe reaches a second documentcarrying this claim, and that document has been right all along — so a zero elsewhere is a
reading, not a blind spot.
docs/audits/2026-07-app-metadata-reference-integrity-assessment.md:93("warning throughout",stamped "Landed 2026-07-28") records what shipped on that date;
.changeset/16310-orphan-locale-key-gates.md,packages/lint/CHANGELOG.md,packages/cli/CHANGELOG.mdandcontent/docs/releases/**are release-owned and correct as oftheir entries. AGENTS.md forbids amending them from a code PR.
content/docs/ui/translations.mdx:242-244(default-localeCOVERAGE severity) and
:346-365(theflowsgroup's liveness warning) are different rules, notthis claim.
Part ofand nota closing keyword.
Verification
Gate families derived mechanically from the change set rather than listed by hand:
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat commit4083936f7, then reconciled with--rancarrying one recorded exit code per family:Three families first answered exit 3 = PREREQUISITE NOT MET (
@objectstack/lintand@objectstack/client-reactwere unbuilt in a fresh worktree), which is not a finding. They werere-run after building those packages and all three then exited 0:
check:doc-formula-expressions,check:doc-security-posture,check:docs-transcript-drift, pluscheck:skill-exampleswhich hadexited 1 for the same unbuilt-
distreason it prints by name.Notable members of the 39, all exit 0:
check:docs-section-name,check:doc-anchors,check:doc-authoring,check:docs-single-h1,check:doc-frontmatter,check:corpus-claim-drift,check:docs-audit-scope,check:cross-package-test-inputs,check:nul-bytes,pnpm --filter @objectstack/spec check:docs.Also self-scanned for control bytes beyond the gate, per AGENTS.md:
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'over the edited file returns nothing (exit 1).Declared scope of the local run: repository-wide scans (
pnpm lintforemost) are CI's run, andorigin/mainmoved 4 commits after this branch's base. The only gate family those commits add ischeck:rerun-safety-verdict, a self-test-only checker-health family wired whole-tree and unrelatedto
content/docs; they add no docs-path family (measured by diffing.github/workflows/lint.ymland
package.jsonacross that range). CI derives on the merge ref.Changeset: none, and the measurement behind that
The criterion is whether anything PUBLISHED moves — what each package's
files[]actually ships.Measured over every non-private
package.jsonin the workspace: 70 published packages, 0 whosefiles[]namescontent/docs; positive control on the same instrument, 70 of 70 namedist.apps/docs(the Fumadocs site that renders this page) is"private": true. Nothing copiescontent/docsinto a package at build time. Henceskip-changeset.Acceptance notes
Observed while sweeping, NOT fixed here and NOT filed as cards:
packages/lint/src/validate-translatable-sections.ts:40-43justifies its ownwarningseveritywith "Same reading as its sibling rule (ADR-0072 D1): nothing crashes and nothing is dead". The
sibling is
validate-translation-references, which no longer holds that reading — this is thesame falsified claim, one file over, by reference. Its own rule's warning severity is still
correct; only the cross-reference is stale.
packages/lint/**is fenced for this card by thetriage ruling and by the dispatch, so it is reported rather than touched, and it is why this body
reads
Part of #18452— the docs half is complete, the card's last carrier is not. The repoalready carries the correction one file over, in
packages/lint/src/reference-integrity-suite.ts:406-410: "warning-only on its own reading - NOTon its sibling's any more".
packages/lint/src/validate-translation-references.test.ts:26-27narrates the superseded Severitynote in the present tense inside a docblock; harmless in context (the tests below it assert
error), noted only because it is the third text in the family.Neither is a reproducible defect, a declared-contract violation or a metadata-authoring trap, so
neither is filed by this PR; both are handed to triage in the report instead.
Generated by Claude Code