You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
i18n: a field's translated help never shows when the field authors inlineHelpText — the server serves fields.FIELD.help on description and the console draws inlineHelpText first #29153
On a zh-CN session, the showcase contact Edit form shows the lead_score help line in English, "Computed by scoring rules — not user-editable.", although the zh-CN bundle carries objects.showcase_contact.fields.lead_score.help = 由评分规则计算,不可手动编辑。.
GET /api/v1/meta/object/showcase_contact with Accept-Language: zh-CN serves fields.lead_score as {inlineHelpText: "Computed by scoring rules — not user-editable.", description: "由评分规则计算,不可手动编辑。"}, and the console's help reader picks inlineHelpText.
Measured at framework 7fa9873f (main, 2026-11-02) on a stock showcase boot, console built from the same tree.
Reproduction
Boot the showcase; sign in as admin; avatar menu → Language → 中文(中国)(writes sys_user.locale = zh-CN).
Open /_console/apps/com.example.showcase/showcase_contact?form=CONTACT_ID (any contact; the default form).
Expected: the 线索评分 help line reads 由评分规则计算,不可手动编辑。.
Actual:Computed by scoring rules — not user-editable.
translateObject serves the bundle's field help only on description (packages/spec/src/system/i18n-resolver.ts:4446-4452); its docblock (~:4383-4394) says help is "not served on inlineHelpText either, which the extractor never reads".
Read fresh. A field's translated help never shows when the field authors inlineHelpText: the server serves the translation on description, and the console reads inlineHelpText first. Every translation pack's help is masked by the source text. This FAILs i18n.surface-matrix negative[1] on the release checklist, so it is p1 (「发版本优先的都p1」). Platform first: the i18n resolver (translateObject in packages/spec) lands the help translation on the key the author wrote. domain:spec.
Claim: PM loop round 1 · 2026-11-02T08:33Z
Session: session_01ADHkmPi25XXKx92Xmg4viS (seat); dev = card thread session_01ADHkmStM2XRLNZKJCmbBaA
Account: os-steve
Branch: claude/project-thread-p9v8u9
Worktree: the card thread's own checkout of objectstack-ai/objectstack
Domain: domain:spec
Seat: domain:spec#1
File surface: packages/spec/src/system/i18n-resolver.ts (translateObject's field-help branch and its docblock) and its tests; regenerated references the spec gates name; root .changeset/. Not ui/packages/fields and not packages/cli unless the contract can't be met from the resolver (stop on breach; explain in the report)
Container & model: S, mode:thread, model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); one CONTRACT_REVIEW_TIER review (a served translated read changes) before landing, effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: packages/spec i18n-resolver.ts translateObject field help | #25683 made the extractor scaffold fields.F.help from help ?? inlineHelpText ?? description, but translateObject still serves the bundle's help on description only, and the console's reader draws inlineHelpText first | every reader of a non-source locale on a field that authors inlineHelpText (showcase lead_score, the Studio field panel's own key)
Thread-read: none
Premise: holds (the card measured at framework 7fa9873f: GET /api/v1/meta/object/showcase_contact with Accept-Language: zh-CN serves lead_score with the English inlineHelpText and the zh-CN text on description; the console reads inlineHelpText || description)
Serial constraints cleared: none (the seat's other in-flight cards #29067, #28914, #28912, #28993 and #29023 hold disjoint files; #28993 edits translation.zod.ts, not the resolver)
Ruling-ref: the seat's contract ruling in the delta: the translated help is served on the slot the console reads first, one derivation across extractor, resolver and console. No new key. Metadata-protocol: none, because nothing changes in what an author can write (AGENTS.md checklist 3); the served translation changes, and the changeset says so.
{
"issue": 29153,
"status": "done",
"branch": "claude/project-thread-p9v8u9",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29217",
"session": "session_01ADHkmStM2XRLNZKJCmbBaA",
"premise_still_valid": true,
"summary": "translateObject now serves the bundle's objects.O.fields.F.help entry on the field's authored, non-empty inlineHelpText (the slot the console's fieldHelpText draws first) as well as on description (unchanged, per the seat's ruling). The new slot uses the same per-slot valueOverridesPackagedBase divergence rule as description; no new key, no second fold, nothing served on help. The stale docblock ('not served on inlineHelpText either, which the extractor never reads') is rewritten as the three-place derivation. Premise re-measured at 0d6d4061: the branch at L4447 wrote description only; FieldSchema declares inlineHelpText (field.zod.ts L2447) and refuses help as an alias of inlineHelpText.",
"tests": "head e1451dc0. @objectstack/spec test: Test Files 790 passed (790), Tests 23377 passed | 1 todo; spec typecheck exit 0 (os-verify-lock batch, both parts 0). Consumers: @objectstack/rest full suite Test Files 367 passed (367), Tests 6963 passed | 332 skipped; @objectstack/metadata-protocol full suite Test Files 298 passed | 4 skipped, Tests 22069 passed; @objectstack/cli --project unit test/i18n-extract-inline-help-text.test.ts 6/6; @objectstack/objectql src/engine-formula-option-label.test.ts 7/7; ui/ (from ui root) packages/fields/src/__tests__/fieldHelpText-12213.test.tsx 5/5. Ablation (tests import src, no dist): scripts/ablation-replace.mjs replaced 'next.inlineHelpText = translatedHelp;' (anchor 1 to 0, blob 0241a7c5ab2e to 5fc0e733d89c), i18n-resolver.test.ts 3 failed / 377 passed (phone both-slots, lead_score showcase, no-packaged-base), restored blob == HEAD and git diff HEAD empty.",
"gates": "dispatch-gates --commands at e1451dc0: 79 derived. Run locally after the last commit, exits captured before any pipe: 77 exit 0; node scripts/check-plugin-teardown-shape.mjs --self-test exit 3 (shallow clone, fixture commit 621a4876 unreachable) to fetched that commit, rerun exit 0 (48 cases); pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (no dist for 10 packages outside this diff's closure) = NOT MEASURED locally, CI Lint & Repo Gates carries it. --ran reconciliation with exit codes: 79 derived, 78 run, 1 NOT-MEASURED, 0 UNRUN. spec build + check:generated: all 14 generated artifacts up to date. Offline check-changeset-no-major --event on the PR body: exit 0, LEVEL AXIS patch, Clause-② no, Metadata-protocol none. Local-only check:dev-prereqs exit 1 (35 ui/ workspace packages unbuilt in this worktree; local state, not a diff finding). CI check-runs on PR 29217: in_progress at report time, not waited on.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "2 — mcp__github__issue_read get_comments (Claim check), mcp__github__pull_request_read get (PR read-back); no write tool",
"api_writes": "2 — fleet-write relay pr_create (PR 29217, 7312 bytes sent = stored), post-stamped os-dev-report comment on #29153; plus git push (not REST)",
"files_changed": [
"packages/spec/src/system/i18n-resolver.ts",
"packages/spec/src/system/i18n-resolver.test.ts",
".changeset/29153-field-help-on-inline-help-text.md"
],
"line_budget": "+222 / -34 over 3 files (resolver +53/-27 incl. docblock, test +166/-7, changeset +14); under the 3000 human-merge threshold",
"deviations": [
"Forced test edit: the phone fixture in the existing '#21948' describe block carried en/zh help entries derived from description, which the extractor no longer writes for a field authoring inlineHelpText; fixture now holds the inlineHelpText-derived text and the 'inlineHelpText is untouched' test pins both slots. Named in the PR body.",
"The session context asks for MCP issue_write/update_pull_request to assign os-steve and request review; standard order item 3 and os-dev forbid MCP writes and PR assignees, so both were skipped.",
"Worktree and node_modules kept (not removed after PR open) so a FAIL round can run without a fresh install.",
"Consumer for the served object read is packages/rest (meta-item-read-gate.ts / form-routes.ts call translateMetadataDocument), not packages/core or packages/metadata; ran rest and metadata-protocol full suites plus objectql's translateObject caller."
],
"open_questions": [
{
"question": "When a field authors both inlineHelpText and a DIFFERENT description, the help entry is the translation of inlineHelpText (extractor source), yet it is also served on description, so description shows the inlineHelpText's translation. Pre-existing since #25683; the ruling keeps it. Should description stop receiving the entry when the field authors inlineHelpText?",
"options": [
"A keep as is (this PR, per ruling): description keeps today's behaviour; the console draws inlineHelpText, so the visible help line is right",
"B follow-up card: serve the entry on description only when the field authors no non-empty inlineHelpText; description then stays in the source language for such fields (no bundle key translates it)"
],
"recommendation": "A for this card; B only if a consumer that draws description (tooltip/docs) is shown to mislead, since B leaves description untranslated"
}
],
"out_of_scope_findings": [
"class: a · reach: named producer: collectExpectedEntries in packages/cli/src/utils/i18n-extract.ts (behind os i18n extract), measured at e1451dc0 with tsx: a field {inlineHelpText: '', description: 'D'} yields the objects.kpi.fields.score.help entry with no sourceValue/inline (no seed scaffolded), while {description: 'D'} seeds 'D'; the console (fieldHelpText, ||) draws description for the first field, so its description is never offered for translation · evidence: the chain is help ?? inlineHelpText ?? description (L1489), ?? stops at '' · dedupe words: i18n extract inlineHelpText empty string, help seed nullish, fieldHelpText || vs ??",
"carrier: seat (domain:spec#1) to file or fold into an i18n extractor card; not filed by this thread"
]
}
pm:dispatched is removed. Seat ACCEPT 6574410876 and a same-head CONTRACT_REVIEW_TIER PASS on PR #29217. translateObject now writes the translated help into the inlineHelpText slot the console reads first, and description keeps its own translation per the ruling. The review's should-fixes S1/S2 and Edge 2 of #29221 are carried by #29286, which is now free to dispatch. Edge 1 of #29221 is fixed by PR #29280.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p1 ·
domain:spec·area:i18n·pm:queue· 2026-11-02T08:32Z⛔ Not a claim.
inlineHelpText: the server serves the translation ondescription, and the console readsinlineHelpTextfirst. Every translation pack's help is masked by the source text. This FAILsi18n.surface-matrixnegative[1] on the release checklist, so it is p1 (「发版本优先的都p1」). Platform first: the i18n resolver (translateObjectinpackages/spec) lands the help translation on the key the author wrote.domain:spec.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS
Claim: PM loop round 1 · 2026-11-02T08:33Z
Session:
session_01ADHkmPi25XXKx92Xmg4viS(seat); dev = card threadsession_01ADHkmStM2XRLNZKJCmbBaAAccount:
os-steveBranch:
claude/project-thread-p9v8u9Worktree: the card thread's own checkout of
objectstack-ai/objectstackDomain:
domain:specSeat:
domain:spec#1File surface:
packages/spec/src/system/i18n-resolver.ts(translateObject's field-help branch and its docblock) and its tests; regenerated references the spec gates name; root.changeset/. Notui/packages/fieldsand notpackages/cliunless the contract can't be met from the resolver (stop on breach; explain in the report)Container & model:
S,mode:thread,model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); one CONTRACT_REVIEW_TIER review (a served translated read changes) before landing,effort: highClause-②: no
Metadata-protocol: none
Responsibility:
packages/spec i18n-resolver.ts translateObject field help | #25683 made the extractor scaffold fields.F.help from help ?? inlineHelpText ?? description, but translateObject still serves the bundle's help on description only, and the console's reader draws inlineHelpText first | every reader of a non-source locale on a field that authors inlineHelpText (showcase lead_score, the Studio field panel's own key)Thread-read: none
Premise: holds (the card measured at framework
7fa9873f:GET /api/v1/meta/object/showcase_contactwithAccept-Language: zh-CNserveslead_scorewith the EnglishinlineHelpTextand the zh-CN text ondescription; the console readsinlineHelpText || description)Serial constraints cleared: none (the seat's other in-flight cards #29067, #28914, #28912, #28993 and #29023 hold disjoint files; #28993 edits
translation.zod.ts, not the resolver)Ruling-ref: the seat's contract ruling in the delta: the translated help is served on the slot the console reads first, one derivation across extractor, resolver and console. No new key.
Metadata-protocol: none, because nothing changes in what an author can write (AGENTS.md checklist 3); the served translation changes, and the changeset says so.objectstack-fleet commentedon Nov 2, 2026
domain:spec#1 · os-steve · session_01ADHkmStM2XRLNZKJCmbBaA
os-dev-report
{ "issue": 29153, "status": "done", "branch": "claude/project-thread-p9v8u9", "pr": "https://github.com/objectstack-ai/objectstack/pull/29217", "session": "session_01ADHkmStM2XRLNZKJCmbBaA", "premise_still_valid": true, "summary": "translateObject now serves the bundle's objects.O.fields.F.help entry on the field's authored, non-empty inlineHelpText (the slot the console's fieldHelpText draws first) as well as on description (unchanged, per the seat's ruling). The new slot uses the same per-slot valueOverridesPackagedBase divergence rule as description; no new key, no second fold, nothing served on help. The stale docblock ('not served on inlineHelpText either, which the extractor never reads') is rewritten as the three-place derivation. Premise re-measured at 0d6d4061: the branch at L4447 wrote description only; FieldSchema declares inlineHelpText (field.zod.ts L2447) and refuses help as an alias of inlineHelpText.", "tests": "head e1451dc0. @objectstack/spec test: Test Files 790 passed (790), Tests 23377 passed | 1 todo; spec typecheck exit 0 (os-verify-lock batch, both parts 0). Consumers: @objectstack/rest full suite Test Files 367 passed (367), Tests 6963 passed | 332 skipped; @objectstack/metadata-protocol full suite Test Files 298 passed | 4 skipped, Tests 22069 passed; @objectstack/cli --project unit test/i18n-extract-inline-help-text.test.ts 6/6; @objectstack/objectql src/engine-formula-option-label.test.ts 7/7; ui/ (from ui root) packages/fields/src/__tests__/fieldHelpText-12213.test.tsx 5/5. Ablation (tests import src, no dist): scripts/ablation-replace.mjs replaced 'next.inlineHelpText = translatedHelp;' (anchor 1 to 0, blob 0241a7c5ab2e to 5fc0e733d89c), i18n-resolver.test.ts 3 failed / 377 passed (phone both-slots, lead_score showcase, no-packaged-base), restored blob == HEAD and git diff HEAD empty.", "gates": "dispatch-gates --commands at e1451dc0: 79 derived. Run locally after the last commit, exits captured before any pipe: 77 exit 0; node scripts/check-plugin-teardown-shape.mjs --self-test exit 3 (shallow clone, fixture commit 621a4876 unreachable) to fetched that commit, rerun exit 0 (48 cases); pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (no dist for 10 packages outside this diff's closure) = NOT MEASURED locally, CI Lint & Repo Gates carries it. --ran reconciliation with exit codes: 79 derived, 78 run, 1 NOT-MEASURED, 0 UNRUN. spec build + check:generated: all 14 generated artifacts up to date. Offline check-changeset-no-major --event on the PR body: exit 0, LEVEL AXIS patch, Clause-② no, Metadata-protocol none. Local-only check:dev-prereqs exit 1 (35 ui/ workspace packages unbuilt in this worktree; local state, not a diff finding). CI check-runs on PR 29217: in_progress at report time, not waited on.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "2 — mcp__github__issue_read get_comments (Claim check), mcp__github__pull_request_read get (PR read-back); no write tool", "api_writes": "2 — fleet-write relay pr_create (PR 29217, 7312 bytes sent = stored), post-stamped os-dev-report comment on #29153; plus git push (not REST)", "files_changed": [ "packages/spec/src/system/i18n-resolver.ts", "packages/spec/src/system/i18n-resolver.test.ts", ".changeset/29153-field-help-on-inline-help-text.md" ], "line_budget": "+222 / -34 over 3 files (resolver +53/-27 incl. docblock, test +166/-7, changeset +14); under the 3000 human-merge threshold", "deviations": [ "Forced test edit: the phone fixture in the existing '#21948' describe block carried en/zh help entries derived from description, which the extractor no longer writes for a field authoring inlineHelpText; fixture now holds the inlineHelpText-derived text and the 'inlineHelpText is untouched' test pins both slots. Named in the PR body.", "The session context asks for MCP issue_write/update_pull_request to assign os-steve and request review; standard order item 3 and os-dev forbid MCP writes and PR assignees, so both were skipped.", "Worktree and node_modules kept (not removed after PR open) so a FAIL round can run without a fresh install.", "Consumer for the served object read is packages/rest (meta-item-read-gate.ts / form-routes.ts call translateMetadataDocument), not packages/core or packages/metadata; ran rest and metadata-protocol full suites plus objectql's translateObject caller." ], "open_questions": [ { "question": "When a field authors both inlineHelpText and a DIFFERENT description, the help entry is the translation of inlineHelpText (extractor source), yet it is also served on description, so description shows the inlineHelpText's translation. Pre-existing since #25683; the ruling keeps it. Should description stop receiving the entry when the field authors inlineHelpText?", "options": [ "A keep as is (this PR, per ruling): description keeps today's behaviour; the console draws inlineHelpText, so the visible help line is right", "B follow-up card: serve the entry on description only when the field authors no non-empty inlineHelpText; description then stays in the source language for such fields (no bundle key translates it)" ], "recommendation": "A for this card; B only if a consumer that draws description (tooltip/docs) is shown to mislead, since B leaves description untranslated" } ], "out_of_scope_findings": [ "class: a · reach: named producer: collectExpectedEntries in packages/cli/src/utils/i18n-extract.ts (behind os i18n extract), measured at e1451dc0 with tsx: a field {inlineHelpText: '', description: 'D'} yields the objects.kpi.fields.score.help entry with no sourceValue/inline (no seed scaffolded), while {description: 'D'} seeds 'D'; the console (fieldHelpText, ||) draws description for the first field, so its description is never offered for translation · evidence: the chain is help ?? inlineHelpText ?? description (L1489), ?? stops at '' · dedupe words: i18n extract inlineHelpText empty string, help seed nullish, fieldHelpText || vs ??", "carrier: seat (domain:spec#1) to file or fold into an i18n extractor card; not filed by this thread" ] }Stamped 2026-11-02T09:40Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS
Landed · 2026-11-02T11:55Z
20f052d0da, through the merge queuecompletedbyFixes #29153pm:dispatchedis removed. Seat ACCEPT 6574410876 and a same-head CONTRACT_REVIEW_TIER PASS on PR #29217.translateObjectnow writes the translated help into theinlineHelpTextslot the console reads first, anddescriptionkeeps its own translation per the ruling. The review's should-fixes S1/S2 and Edge 2 of #29221 are carried by #29286, which is now free to dispatch. Edge 1 of #29221 is fixed by PR #29280.