Uh oh!
There was an error while loading. Please reload this page.
fix(lint): descend into conditional then/otherwise when building the _validations universe - #14820
Conversation
…_validations universe validate-translation-references built its `_validations` universe with a flat walk of `objects[].validations[]`, so a `conditional` rule's `then` / `otherwise` branch — a full rule carrying its own `name`, which IS the address `checkConditional` delegates to and `authoredRuleMessage` keys on at runtime (packages/objectql/src/validation/rule-validator.ts) — was reported as an orphan `translation-target-unknown`, with inverted advice (acting on it reintroduces the exact defect the entry fixed). The walk now descends into `then` / `otherwise` via a small recursive collector, mirroring `evaluateRule`'s recursion (a branch may itself be a nested `conditional`, so depth is unbounded). The wrapper's own name stays in the universe unchanged, since a bundle entry for it is kept deliberately elsewhere. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
…ditional Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
📓 Docs Drift Check3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 5 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 28e6348288a4174df92fb64f5fc8409ac0fcd2c6 && git checkout 28e6348288a4174df92fb64f5fc8409ac0fcd2c6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2d40f9146800dcf625fc54696f6435e676b1ed96 c2095e53852f6702f3625c348f92067f254e1282 && git checkout -B drift-repro 2d40f9146800dcf625fc54696f6435e676b1ed96 && git merge --no-ff c2095e53852f6702f3625c348f92067f254e1282
node scripts/docs-audit/affected-docs.mjs --json 2d40f9146800dcf625fc54696f6435e676b1ed96 |
Re-trigger via PR-side CI on Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#14700
What
validate-translation-referencesbuilt its_validationsuniverse with a flat walk ofobjects[].validations[]. Aconditionalrule'sthen/otherwisebranch is itself a full rule carrying its ownname, and that branch name — not the wrapper's — is the addresscheckConditionaldelegates to andauthoredRuleMessagekeys on at runtime (packages/objectql/src/validation/rule-validator.ts). The flat walk never saw a branch name, so a correct bundle entry for one was reported as an orphantranslation-target-unknown, and the finding's own advice ("keeps its source locale in every refusal") was the opposite of the truth for that key — acting on it (deleting the entry) reintroduces the exact defect it fixed.Fix
buildUniverse's walk overobj.validationsnow calls a small recursive collector (collectValidationRuleNames) instead of readingrule.namedirectly:This mirrors
evaluateRule's recursion (checkConditionaldispatches the branch back throughevaluateRule, which recurses intocheckConditionalagain for a nestedconditional) — depth is unbounded, matching the runtime.The wrapper's own name stays in the universe, unchanged. Its message is structurally unreachable (
checkConditionaleither returns nothing, returnsunevaluableRuleError— which builds its own sentence — or delegates to the branch), so an entry for it is inert at runtime. #14518 keeps one anyway, deliberately, so the bundle mirrors the declared rule set 1:1 rather than re-deriving objectql's dispatch — this PR does not touch that decision.packages/objectql/**is untouched:checkConditional/authoredRuleMessageare the addressing this mirrors, not changes.Before / after (reproduces the card's own measurement)
Same fixture as the issue —
demo_accountwith oneconditionalrule (churn_reason_consistency) whosethen/otherwisearechurn_reason_present/churn_reason_absent, and a bundle with entries for all three names:findings.lengthorigin/main(e6ac0c6fd5, pre-fix)translation-target-unknownonchurn_reason_presentandchurn_reason_absentReproduced by importing
validateTranslationReferencesdirectly (viatsx) from a comparison worktree checked out at the pre-fix commit and from this branch, against the identical fixture object — not just asserted in a test, run both ways.Fixtures added (
validate-translation-references.test.ts, new describe block#14700)churn_reason_consistency,churn_reason_present,churn_reason_absentchurn_reason_ghostchurn_reason_ghostonly (real orphans still caught)conditionalwhosethenis itself aconditional)innermost_rule(2 levels deep)gate(wrapper only; branch has noname)All 64 tests in the file pass, including the pre-existing
object-branch coverage vs the schema (#13835)pin, which still asserts one working leg perObjectTranslationDataSchemakey including_validations.Gates
pnpm --filter @objectstack/lint test— 93 test files / 2845 passed, 5 skipped (pre-existing skips, unrelated).pnpm --filter @objectstack/lint typecheck— clean (tsc --noEmit+check:test-typecheck).eslint --no-inline-configon both touched files — 0 errors / 0 warnings.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran <record>at413167ed8c(post-merge-of-main) — 34 derived, 30 run (all green), 4 NOT-MEASURED, 0 UNRUN:check-test-completeness— grades a savedturbo run testlog; none exists locally (CI tees one).check-half-states(plain, live sweep) — timed out at 180s reaching GitHub through the session proxy; report-only, never fails a build (its own header says so); thecheck:pm-half-statesself-test alias ran and passed.check:dual-build-cjs-loads— reads builtdist/across ~80 workspace packages; fullpnpm buildnot run in this worktree.check:type-check-debt—--re-measurerefuses without the full package build closure, by design (a number measured without it would count a different world, per its own message).None of the 4 relate to this diff's file surface (
packages/lint/src/validate-translation-references.{ts,test.ts}); all are repo-wide gates whose local prerequisite is a full monorepo build/test run, which CI performs.Changeset
.changeset/nested-conditional-validation-refs.md—@objectstack/lintpatch.🤖 Generated with Claude Code
https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Generated by Claude Code