Repository navigation
fix(spec): a field authoring inlineHelpText keeps its source-language description - #29363
Conversation
… description translateObject served a field's `help` translation on `description` and on an authored `inlineHelpText`. The extractor seeds that entry from `help ?? inlineHelpText ?? description`, so on a field authoring both texts every reader of `description` showed the inlineHelpText's translation. The entry is now served on the one slot it was seeded from: `inlineHelpText` when the served or packaged field authors a non-empty one, `description` only otherwise. Also declares `inlineHelpText` on ObjectFieldLike, and makes the source-locale CONTROL test able to fail (its `en` help entry repeated the authored text). Claude-Session: https://claude.ai/code/session_01ADHkmTFqMHMhCKttxJqEQk Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 168 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 5320b2addc210077072a7a2a15002653cacd0994 && git checkout 5320b2addc210077072a7a2a15002653cacd0994
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5924e5002f4a38346ad435514a1282312927658b 320fead6d983a0d843b2a2c04acad038024a29b1 && git checkout -B drift-repro 5924e5002f4a38346ad435514a1282312927658b && git merge --no-ff 320fead6d983a0d843b2a2c04acad038024a29b1
node scripts/docs-audit/affected-docs.mjs --json 5924e5002f4a38346ad435514a1282312927658b
|
…read The Before. bullet described an unreleased state; both notes publish in the same release, so it now states what released 18.2.0 serves. Claude-Session: https://claude.ai/code/session_01ADHkmTFqMHMhCKttxJqEQk Co-authored-by: Claude <noreply@anthropic.com>
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Head-sha: 维护者速读
席位意见:可落地。 席位核对 — ACCEPT ·
|
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Contract-review-claim: session_01ADHkmPi25XXKx92Xmg4viS · head 320fead · 2026-11-02T13:57Z |
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Contract reviewServed-tier: Delta review over the head reviewed at ① Derived judgmentsThe delta — the "Before." bullet of
Consistency with the corrected 29153 note (unchanged on this head). Both "Before." bullets describe 18.2.0 from the two readers' sides: 29153 from the console's (the Carried forward from the record at Check-runs on this head, complete at 2026-11-02T13:54Z: 64 total, 54 success, 9 skipped, 1 failure. The failure is ② Semver level
The deliberate correction of the pending ③ Boundary flags
④ Design judgment
Findings:
Implemented-by: VERDICT: PASS |
Requested by Steve · project thread
Fixes #29286
Clause-②: no
Metadata-protocol: none
维护者速读
translateObject把字段的help译文只放到它的来源槽位:字段(服务端或打包声明)写了非空inlineHelpText时只译inlineHelpText,description保持原文;都没写时才译description。另在ObjectFieldLike上声明inlineHelpText,并让源语言的 CONTROL 测试真正可失败。help ?? inlineHelpText ?? description取help原文;同时写了两段不同文字的字段,Studio 和设计器读description时显示的是inlineHelpText的译文,一句话被换成了另一句话。description的字段照旧被翻译,租户改过的值照旧保留,作者能写什么不变。仓库里没有发布的对象同时写这两段文字(只有 showcaselead_score写了inlineHelpText,它没有description)。回滚即撤掉本 PR。packages/spec/src/system/i18n-resolver.ts中translateField的 help 分支与新的authorsInlineHelpText;i18n-resolver.test.ts中两个translateObjecthelp describe;.changeset/29153-field-help-on-inline-help-text.md改的那一句。pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/system/i18n-resolver.test.ts,应看到 383 条全过。What changes
translateObject(packages/spec/src/system/i18n-resolver.ts) serves a field'sobjects.OBJECT.fields.FIELD.helptranslation on ONE slot, the one the i18n extractor seeded the entry from (seat direction A on #29286, no new public name):inlineHelpTextgets the translation there, under the per-slot divergence rule as before, and keeps its source-languagedescription; a field with onlyinlineHelpTextgains nodescription;descriptiontakes the translation only when neither the served field nor the packaged field authors a non-emptyinlineHelpText(an empty string reads as absent, as in the extractor and the console), and still only while it equals the packaged field'sdescription(valueOverridesPackagedBase, unchanged);inlineHelpTextblanked over a packaged one leaves an entry that still translates the packagedinlineHelpText, sodescriptionstays in its source language. With no packaged base, only the served field is asked.The rule lives in one new private helper,
authorsInlineHelpText(field), asked of the served and the packaged field. ThetranslateObjectdocblock section is rewritten to state it, and theObjectFieldLikedocblock no longer states the old both-slots rule.S1.
ObjectFieldLikedeclaresinlineHelpText?: stringbesideinlineTitle. It was reached only through the index signature.S2. The source-locale CONTROL test could not fail: the
enbundle'slead_score.helprepeated the authored text, so serving it and leaving the field untouched gave the same answer. Theenhelpentry is dropped (a comment says why), and the test is renamed to what it pins now: the source locale, with nohelpentry for the field whilezh-CNhas one, serves the authoredinlineHelpTextand never another locale's. Ablation below.Pending changeset corrected.
.changeset/29153-field-help-on-inline-help-text.md(unreleased, from PR #29217) said the translation is served oninlineHelpText"and ondescriptionas before". This PR makes that false, so its Now. bullet is corrected in the same stroke.check-empty-changeset.mjsrefuses this by design as the DELIBERATE CORRECTION class: please confirm it on the PR; do not restore the old sentence. The new changeset is.changeset/29286-field-help-description-slot.md(spec patch).Forced test edits (old behaviour pinned)
All in
packages/spec/src/system/i18n-resolver.test.ts:a field declaring both description and inlineHelpText gets the translation on both…(fixturephone) now assertsdescriptionstaysContact number.; renamed to…gets the translation on inlineHelpText only, the slot the entry was seeded from.keeps a description that diverged…: the per-field sibling assertion moves fromphone.descriptiontophone.inlineHelpText.judges an array-shaped field list…: the same move forphone.serves the showcase lead_score translation on its authored inlineHelpText…: assertslead_scoregains nodescription(was the translation).New tests (fixture
vat_number, authoring both texts): the zh-CN read keepsdescription; the same with no packaged base; a servedinlineHelpTextblanked over the packaged one keepsdescription.Tests
All readings at head
b639a5b4, onobjectstack-ai/objectstack, merge base5a2bdf9c1.vat_numbertests plus alead_scoregains-no-descriptiontest, run against the unfixed resolver:Tests 4 failed | 11 passedin the…reaches the slot the console draws firstdescribe (expected '个人客户请留空。' to be 'Tax id used on invoices.').pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/system/i18n-resolver.test.ts:Tests 383 passed (383).pnpm --filter @objectstack/spec test(under os-verify-lock,VERDICT command-exit 0):Test Files 791 passed (791),Tests 23394 passed | 1 todo.pnpm --filter @objectstack/spec typecheck:VERDICT command-exit 0(test layer: 52 files / 240 errors held intest-typecheck-debt.json, unchanged).pnpm --filter @objectstack/spec check:generated:All 14 generated artifacts are up to date. Nothing to regenerate: no schema,.describe()or export changed.pnpm --filter @objectstack/rest test(the served object reads,meta-item-read-gate.ts/form-routes.ts, runtranslateObject):Test Files 368 passed (368),Tests 6982 passed | 332 skipped.Ablations (one-shot, through scripts/ablation-replace.mjs, restored to the HEAD blob)
Spec tests import the resolver from
src/, so no dist leg applies.if (translatedHelp) {becomesif (true as boolean) {(the no-entry guard dropped; anchor 1 to 0, blob848830f5to5ad5b046).× CONTROL: the source locale, which carries no help entry…(Tests 1 failed).git show 5a2bdf9c:…/i18n-resolver.test.tsas a temporary copy, deleted after), same mutation:Tests 1 passed. Itsenentry repeated the authored text, so the mutation was invisible to it.git diff HEADempty.!authorsInlineHelpText(packagedField) &&becomestrue &&. Result:× a served inlineHelpText blanked over a packaged one still keeps description in its source language(1 failed | 12 passed). Restored: blob == HEAD.Acceptance notes
inlineHelpTextanddescriptionare the same string now keeps its source-languagedescription, although the entry translates that same text. The seat's rule is applied as written; whether to also translatedescriptionwhen it equals the seeded text is in the report's open questions.Verification
Derived gate families: 79 derived, 79 run, 78 exit 0, 1 designed red
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackatb639a5b4(4 paths vs merge base5a2bdf9c1), each command run with stdin closed, then--ran:✓ dispatch-gates --ran: 79 derived famil(ies) accounted for — 79 run, 0 NOT-MEASURED (a DERIVED zero — all 79 recorded an exit code and none of them is 3).The one non-zero exit is
node scripts/check-empty-changeset.mjs --base origin/main :: exit 1, the DELIBERATE CORRECTION refusal for.changeset/29153-field-help-on-inline-help-text.mddescribed above. It is red by design; do not restore the old sentence.Offline PR-body check:
node scripts/check-changeset-no-major.mjs --base origin/main --event FILEwith this body:✓ This diff introduces no major bump,✓ LEVEL AXIS: … clause-② no and metadata-protocol none … a non-protocol change at patch.Dogfood: not run. The title has no
!andClause-②:has no(narrowing)arm.Generated by Claude Code