fix(showcase): put the remaining eight authored validation messages on the translation channel - #14709
Merged
os-musk merged 3 commits intoSep 2, 2026
Conversation
…n the translation channel `showcase_account` declares seven author-written `validations[].message` and `showcase_task` one, and none had an `objects.<o>._validations.<rule>.message` entry. An authored message is emitted VERBATIM without one, so on a zh-CN session those refusals arrived in English beside the platform's own, which have shipped zh-CN since #3957 — two languages inside one `400 VALIDATION_FAILED` envelope. Both nested `conditional` branches get their own entry. `checkConditional` delegates to the matching branch and renders THAT branch's message, addressed by the branch's own `name`, so `churn_reason_consistency`'s own sentence is structurally unreachable; translating only the wrapper would have translated the one sentence nobody reads. Its entry is kept so the bundle mirrors the declared rule set 1:1 and the pin can ask for every declared name rather than re-deriving objectql's dispatch. The pin is now BUNDLE-WIDE rather than per-object. The per-object version this replaces covered `showcase_project` only, which is exactly how these eight were left behind; the new one walks the composed stack's objects and object extensions, descends into conditional branches, and asks the question for every locale `i18n.supportedLocales` claims — so a newly declared rule without a translation fails instead of rotting. It also pins the default-locale entry to the authored sentence verbatim: the bundle wins in every locale, `en` included, so a drifted `en` entry turns the object's own message into dead text. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
…ion-message translations Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
…owcase-authored-validation-translations
os-musk
marked this pull request as ready for review
September 2, 2026 20:31
os-musk
enabled auto-merge
September 2, 2026 20:31
os-musk
deleted the
claude/issue-14518-showcase-authored-validation-translations
branch
September 2, 2026 21:14
os-trump
pushed a commit
that referenced
this pull request
Sep 2, 2026
…#14709 #14709 put the remaining eight authored validation messages on the showcase's translation channel, moving the `objects.OBJECT._validations.RULE.message` population this branch is the first to walk. Re-derived with the repo's own tooling (`node scripts/check-i18n-coverage.mjs --update`), never by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
This was referenced Sep 2, 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.
Fixes #14518
What
showcase_accountdeclares seven author-writtenvalidations[].messageandshowcase_taskone, and none of them had anobjects.OBJECT._validations.RULE.messageentry. An authored message is emitted verbatim without one, so on azh-CNsession those refusals arrived in English beside the platform's own — which have shippedzh-CNsince #3957 — two languages inside one400 VALIDATION_FAILEDenvelope.#14311 fixed the same defect for
showcase_project(PR #14517,bbbac0fc4); its scope was one wizard, so these eight were left behind. This addsen+zh-CNentries for all eight and replaces the per-object pin with a bundle-wide one.The nested-
conditionalsubtlety, verified against the codechurn_reason_consistencyis aconditionalwith both branches. Its own message never reaches a caller:checkConditional(packages/objectql/src/validation/rule-validator.ts) either returns nothing, returnsunevaluableRuleError— which builds its own sentence and ignoresrule.message— or delegates to the branch, andauthoredRuleMessagekeys on the branch'srule.name. The source says so in its own words: "rule.nameis the address — including a nestedconditionalbranch's own name, since the branch is the rule whose message the caller sees."rule-validator.test.tspins it (addresses a nested conditional branch by the BRANCH's own name).So
churn_reason_presentandchurn_reason_absenteach get their own entry — translating only the wrapper would have translated the one sentence nobody reads. The wrapper's entry is kept anyway, so the bundle mirrors the declared rule set 1:1 and the pin can ask for every declared name rather than re-deriving objectql's dispatch in a test.The pin is bundle-wide, not per-object
The card offered a choice and the stronger option fits inside the declared file surface, so it is taken. The replaced test covered
showcase_projectonly — which is exactly how these eight rotted. The new one, in the same file:stack.objects+stack.objectExtensions), the reachability principleseed.test.tsdocuments — an extension'svalidationsmerge into the target at registration, so they are addressed underextend;conditionalbranches, and nameschurn_reason_presentin its premise test so the recursion cannot silently stop mattering;i18n.supportedLocalesclaims, rather than a hardcoded pair;rule.messagein every locale,enincluded, so a driftedenentry turns the object's own message into dead text no reader ever sees;zhone.A newly declared rule without a translation now fails here instead of rotting.
Not lifted into
packages/lint. The triage suggested establishing whethervalidate-translation-referencescould carry this first. It cannot as it stands, and the reason is itself a defect: that rule builds its_validationsuniverse from a flat walk ofobjects[].validations[], so it reports a nested branch's correct entry as an orphan — measured, filed as #14700.packages/lintis outside this card's declared file surface, so it is filed rather than fixed here.Scope
examples/app-crmis untouched — the card declines to assume the CRM demo is meant to be localised, and this PR does not answer that question either.Verification
All readings on
c194fe53e(final HEAD,origin/mainmerged in).Gates — re-derived on the final tree with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands: 30 commands. 28 exit 0. Two exit 3, which is each gate's own distinct "NOT MEASURED" code, not a finding and not a red:check-test-completeness.mjs— the family names it with no argument and there is no savedturbo run testlog to hand it; its own text says "the local reading for this gate is NOT MEASURED. It is not a red."scripts/pm/check-half-states.mjs— "no reading at all… treat this exit as an unread instrument" (it needs a board read this container's REST channel refuses).Tests —
pnpm --filter @objectstack/example-showcase run typecheck && run test: 27 files, 375 tests, all passing.tsc --noEmit --listFilesconfirms all four edited files are in the typecheck program (1 hit each) rather than excluded.Reverse verification — the new pin was made to go red twice, from a committed tree, each mutation confirmed on disk by grepping the specific text before and after, each restore proven by blob-hash equality against the HEAD blob plus an empty
git diff HEAD. The subject is source (vitest transforms the app's TypeScript directly, nodist/on the path), so no rebuild leg applies here.zh-CNentry for the nested branchchurn_reason_present(anchor 1 before, 0 after) — 2 failed / 6 passed, namingzh-CN: objects.showcase_account._validations.churn_reason_present.message. This is also the proof the walk descends into branches: a flat walk would not have missed it.zh-CNtask message with the English source (old anchor 0 after, injected text 1 on disk) — 1 failed / 7 passed: "showcase_task.task_status_flow in zh-CN is a copy of the source".ESLint — narrowed to the changed paths rather than the repo-wide
pnpm lint, and the narrowing is measured, not assumed: the population is eslint's own verdict per file from--format json(5 entries — the four.tsfiles linted clean, the changeset reported by eslint as "File ignored because no matching configuration was supplied"), and the repo runs oneeslint.config.mjsthat never enables type-aware linting for any file (noparserOptions.project, no typed@typescript-eslintrules — stated and measured with a positive control ateslint.config.mjs:327), so nothing in this diff can move the verdict on a file it does not touch. CI runs the full sweep regardless.Changeset
Derived rather than assumed:
examples/app-showcaseisprivate: trueandpnpm check:published-filesis green, but.changeset/config.jsonsetsprivatePackages: { version: true }, and the closest analogue —450f3e5a8, an examples-only showcasezh-CNtranslation PR — carried"@objectstack/example-showcase": patch. So this carries one too, andskip-changesetis not applied.Filed, not fixed
validate-translation-referencesbuilds its_validationsuniverse from top-level rules only, so a nestedconditionalbranch's legitimate bundle entry is reported as an orphan #14700 —validate-translation-referencesreports a nestedconditionalbranch's legitimate bundle entry as an orphan, with advice inverted from the truth. Warning severity, and its rule id is not thei18n/prefixcheck-i18n-coveragecounts, so no gate is red.🤖 Generated with Claude Code
Generated by Claude Code
Generated by Claude Code