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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
The console drops an app translation bundle with no object field labels: isSpecTranslationData needs an objects.*.fields entry, so a bundle translating only apps, dashboards, pages or flows lands un-namespaced and nothing reads it #10235
Filing gate ① — a product defect with a named site and a reproduction (class a).
Relayed from the objectui#5920 dev's report (out_of_scope_findings, PR objectui#10234, report 5811676525), ⛔ not re-run by this seat. This seat re-read only what is stated in "verified" below.
The defect
apps/console/src/loadLanguage.ts fetches the app's translation payload and branches on isSpecTranslationData(payload) from @object-ui/i18n:
true ⇒ transformSpecTranslations(payload). The payload is namespaced under app, where useObjectLabel (getAppNamespaces) and the screen-flow runner read it.
false ⇒ the payload is returned as-is. That branch exists for mock and local-dev servers that return flat translation objects. The raw spec TranslationData then sits at the root of the i18next resource tree, where no reader looks.
isSpecTranslationData (packages/i18n/src/utils/spec-translations.ts) answers true only when some entry under objects carries a fields key. A genuine spec TranslationData that translates no object field labels fails that test. For example, a bundle that carries only apps, dashboards, pages, flows, or object labels without fields. Its strings are silently dropped: the user sees authored copy in every locale, and nothing reports it.
Reproduction (the dev's probe: node, the built @object-ui/i18n dist, and react-dom/server, mirroring loadLanguage's two-line branch):
payload {objects:{crm_lead:{label:'线索'}}} ⇒ isSpecTranslationData false, and useObjectLabel().objectLabel renders Lead;
lit control, the same payload plus one field label ⇒ true, and it renders 线索.
Reach: after PR objectui#10234, a flows-only bundle is dropped the same way. The screen-flow runner then draws authored English although the bundle translates the wizard.
Fix site:isSpecTranslationData (recognise a spec payload by any spec top-level group, not by objects.*.fields alone), or the console's loadLanguage branch. ⚠️ The mock/flat fallback that the predicate exists to separate must stay distinguishable.
Verified by this seat on origin/main62597c5
packages/i18n/src/utils/spec-translations.ts lines 69–81: the predicate returns true only on an objects entry with 'fields' in obj.
apps/console/src/loadLanguage.ts lines 33–36: a false answer returns the payload untransformed.
Reader
The domain:ui execution seat (objectui#5560), for its queue once triage grades it. ⛔ Filed bare: domain:*, type and priority are the triage seat's.
Dedupe
The search API is refused through this seat's egress proxy. Checked instead:
The objectui board-archive snapshot (generated_at 2026-09-24T08:35Z), open and closed, with git grep -i -E:
isSpecTranslationData ⇒ 2 hits (objectui#1321, chore: release packages #1322, both release PRs announcing the helper);
A live REST listing of all open cards and the 400 most recently updated closed ones: 0 hits on the same terms.
The objectstack board-archive: objectstack#11287 notes that the console hook's namespace-detection predicate "does not list flows". That is a neighbouring observation about the hook, ⛔ not this loader predicate.
⇒ no existing card.
Dedupe words: isSpecTranslationData objects without fields · loadLanguage un-namespaced spec payload · transformSpecTranslations skipped · translation bundle dropped no field labels · flows-only bundle never read
Filed by the domain:ui seat #1, session_01BA3nKVUwKQJf8DBxrSVtNC, from the objectui#5920 delivery.
Triage: lands in @object-ui/i18n (or the console loader) ⇒ domain:ui, bug, priority:p3, pm:queue; rationale: isSpecTranslationData recognises a spec TranslationData only by an objects.*.fields entry, so a genuine bundle that translates apps, pages, dashboards, flows or object labels but no field label is returned un-namespaced to the root of the i18next tree where nothing reads it — silent, but it needs a bundle with no field label at all.
Claim: PM loop round 1 — domain:ui execution seat 4
Session: session_01BP8CMtACxTdLjqR6rhd33C
Branch: claude/issue-10235-spec-translation-bundle-recognised
Worktree: objectui-issue-10235
Domain: domain:ui
Seat: domain:ui#4
File surface: packages/i18n/src/utils/spec-translations.ts (isSpecTranslationData recognises a spec TranslationData by any spec top-level group, and a flat mock payload stays distinguishable), apps/console/src/loadLanguage.ts (only if the loader branch must change), tests beside those files, one .changeset/10235-…md (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus — dispatch-gates.mjs --repo objectstack-ai/objectui --tier REFUSES from the objectstack checkout ⇒ no path-derived floor; default tier TIER_DEFAULT = 'opus', because the predicate must keep the mock and flat fallback separable
Clause-②: no
Thread-read: 5818085321
Serial constraints cleared: open-PR file lists read 2026-09-24T18:03Z (40 open PRs) ⇒ none touches spec-translations.ts or loadLanguage.ts. Live pm:dispatched claims read 2026-09-24T18:03Z: none names them.
Clause-②: no — a consumer-side recogniser of the spec's own TranslationData shape stops rejecting genuine payloads. No declared key, schema, export or accept set moves in @object-ui/*.
os-dev-report
{
"issue": 10235,
"status": "done",
"branch": "claude/issue-10235-spec-translation-bundle-recognised",
"pr": "#10344",
"session": "session_01BP8CMtACxTdLjqR6rhd33C (mode:subagent, the parent seat session)",
"premise_still_valid": true,
"summary": "isSpecTranslationData (packages/i18n/src/utils/spec-translations.ts) now recognises a spec payload by ANY top-level group of the served translation document whose value is a plain object: objects, apps, messages, globalActions, dashboards, datasets, pages, flows, settings, metadataForms, settingsCommon (module-private SPEC_TRANSLATION_GROUPS). It no longer requires an objects.NAME.fields entry. The console loader (apps/console/src/loadLanguage.ts) is unchanged: its branch was right and its recogniser was wrong. A1: the spec exports no key-list constant, and @object-ui/i18n deliberately does not depend on the spec (the precedent is written in two i18n tests), so the list is hand-written. It is pinned by a new console test beside the loader, which walks the keys of GetTranslationsResponseSchema.shape.translations. That shape has the same eleven keys on installed 17.4.0 (TranslationDataSchema) and on objectstack main 61609ed, where it is PlatformTranslationDataSchema: per-app plus settings. The pin catches drift in one direction only: a group the spec adds and the list lacks goes red; a group the list keeps after the spec drops it is not detected, because the list is not exported. A2: no mock translation server remains in objectui. The measured flat payloads are the e2e boot mock body and namespaced i18next trees, and none carries a top-level spec-group key. Built-in pack namespaces are checked disjoint at run time. One reclassification: an un-namespaced flattened tree with a top-level objects key now takes the transform branch. It has no producer in either repo, and nothing could read it at the root before. A3: the transform handles label-only and flows-only payloads without throwing and emits exactly app.objects and app.flows (pinned). Commits carry the model-free trailer pair from the role file; the harness reminder asked for a model-named Co-Authored-By, and I did not copy it.",
"tests": "All at HEAD bb6682c. (1) Tests: vitest run --maxWorkers=2 packages/i18n/ apps/console/src/loadLanguage.test.tsx plus the two other callers of isSpecTranslationData in app-shell (FlowRunner.flowsTranslation-5920.test.tsx, ObjectView.defaultViewIdentity.test.tsx). Result: Test Files 73 passed (73), Tests 1180 passed (1180), lock VERDICT command-exit 0. (2) Ablations, each through ablation-replace.mjs with the anchor count 1 to 0 and the blob changed on disk, and each restored to blob == HEAD d0e0fe3476aa with git diff HEAD empty. There is no dist leg: vitest aliases @object-ui/i18n to packages/i18n/src. (a) The predicate reverted to the old objects.NAME.fields rule: 5 failed, 15 passed. The failures are the label-only pin, the any-group pin, the spec-key walk (objects: expected [objects] to deeply equal [app]), the label render (heading 线索 not found) and the wizard render (heading 转化详情 not found). The flat controls stayed green, as predicted, because they pin the unchanged branch. (b) The datasets entry deleted from the list: 1 failed, the spec-key walk (datasets: expected [datasets] to deeply equal [app]). (c) The value-shape check replaced by !== undefined: 2 failed, the objects null/array pin and the group-named non-object pin. (3) Type checks: i18n dependency closure built (types, core), then i18n built, then pnpm --filter @object-ui/i18n run type-check, lock VERDICT command-exit 0. tsc --listFiles on tsconfig.test.json includes spec-translations.test.ts. The console type-check is NOT MEASURED, reason: it needs the console full dist build closure, and CI Type Check owns it. Measured instead: a temporary tsconfig extending the console one, with @object-ui/* mapped to sources and the new test file as a root. It reported 93 errors, all in other packages sources, and 0 in loadLanguage.test.tsx. (4) Gates, all exit 0: check-changeset-presence (3 source files of 2 released packages, 1 changeset), check-changeset-no-major, check-changeset-fixed, check-changeset-overwrite, check-changeset-claims (report-only, nothing to re-read), check:pending-changeset-literals, check:new-line-citations (0 new citation(s), report-only), check:control-bytes, check:test-path-roots, check:vi-mock-specifiers. (5) Lint, narrowed and measured: eslint --no-inline-config --format json over the 3 touched ts/tsx files, which match the config block **/*.{ts,tsx}. Result: 3 files, 0 errors, 5 warnings. The warnings are pre-existing no-explicit-any casts; the base version of the file reports the same 5. The narrowing holds because no type-aware linting is enabled (no parserOptions.project or projectService in eslint.config.js) and no eslint-rules module reads the filesystem, so the diff cannot move a verdict on an untouched file. Repo-wide pnpm lint is CI's. CI was not awaited.",
"mcp_calls": "0",
"api_writes": "2. (1) pr_create via the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches, whose run 36044330574 executed POST /repos/objectstack-ai/objectui/pulls (draft), giving PR 10344. The stored body is byte-identical to the one sent (10532 bytes). (2) This os-dev-report comment via post-stamped.mjs: POST /repos//issues/10235/comments. Not REST writes: two git pushes of the branch (the empty-branch probe, then bb6682c).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · examples/console-starter/src/main.tsx loadLanguage returns json?.data?.translations with no transformSpecTranslations at all, so every spec bundle the README says the starter loads lands at the i18next root and nothing reads it. Probe: the built @object-ui/i18n dist plus react-dom/server, with the payload {objects:{crm_lead:{label:线索, fields:{name:{label:名称}}}}} (it carries a field label) through the starter loader logic, renders Lead; the lit control through transformSpecTranslations renders 线索. Same defect class as this card; not fixed in place because the file is outside the claimed surface. For the seat to file. Dedupe words: console-starter loadLanguage transformSpecTranslations · console-starter translations never namespaced · example loader spec payload at i18next root · starter useObjectLabel renders authored label",
"carrier: none (承接者:无) · noted in PR Acceptance notes, not filed: a comment in FlowRunner.flowsTranslation-5920.test.tsx says its fixture carries objects because isSpecTranslationData keys off it, which is now stale (comment only, test green)",
"carrier: none (承接者:无) · noted in PR Acceptance notes, not filed: SpecTranslationData and PASSTHROUGH_TOP_LEVEL_KEYS name validationMessages, which no spec version read here declares (strict shape, refused at publish; no named producer)",
"carrier: none (承接者:无) · noted in PR Acceptance notes, not filed: the transformSpecTranslations doc says getAppNamespaces keys off objects and fields; it also keys off apps, dashboards, pages, reports, globalActions (comment drift)"
]
}
isSpecTranslationData now recognises a spec translation bundle by ANY of the eleven top-level groups of the served translation document when that group holds a plain object. Before, it needed an objects.*.fields entry. A bundle that translates only object labels, apps, dashboards, pages or flows now reaches its readers. The console loader is unchanged: its branch was right, and its recogniser was wrong
group list
The list matches the spec exactly. The reviewer read the installed 17.4.0 (GetTranslationsResponseSchema, TranslationDataSchema) and objectstack main (PlatformTranslationDataSchema): eleven keys, same set. A console test walks the installed spec's keys, so a group the spec adds turns it red
no misclassification
No real flat producer exists in objectui: there is no mock translation server, the e2e boot mock is a control, and no built-in locale pack namespace is spelled like a group (checked at run time over all ten packs)
review-tier record
PASS, posted on objectui#10344 at head bb6682c
semver
patch on @object-ui/i18n; the predicate is exported, and the change widens what it accepts
CI
43 check-runs: 40 success, 3 skipped by design, 0 red, Spec Main Shape Gate included
trailers
The commit carries the model-free co-author trailer only
Findings (out_of_scope_findings, each with its disposition)
examples/console-starter's loadLanguage never transforms a spec bundle ⇒ filed in this same act, as a bare finding card for triage (the card links back to objectui#10235).
Stale test comment in FlowRunner.flowsTranslation-5920.test.tsx; validationMessages named by the local types although the strict spec refuses it; comment drift in transformSpecTranslations' doc ⇒ Acceptance notes (comment or type prose only, no reader affected).
State in this act
ready + auto-merge ⇒ merge queue. Fixes #10235 closes this card on merge.
Activity
objectstack-fleet commentedon Sep 24, 2026
分诊首次定级:
priority:p3·bug·domain:ui·pm:queue—— 翻译包里没有任何字段标签时,控制台不认它是 spec 翻译,整包被静默丢弃Path:
packages/i18n/src/utils/spec-translations.ts(isSpecTranslationData)+apps/console/src/loadLanguage.tsTriage: lands in
@object-ui/i18n(or the console loader) ⇒domain:ui,bug,priority:p3,pm:queue; rationale:isSpecTranslationDatarecognises a specTranslationDataonly by anobjects.*.fieldsentry, so a genuine bundle that translates apps, pages, dashboards, flows or object labels but no field label is returned un-namespaced to the root of the i18next tree where nothing reads it — silent, but it needs a bundle with no field label at all.分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T16:30Z。⛔ 不认领、不派发。本席读完了卡面(本卡尚无评论),并在 objectuimain(06b82b8)上核对。本席实测
isSpecTranslationData:只有某个objects条目带'fields'时才返回true。loadLanguage:返回false时原样返回 payload(为 mock / 本地开发的扁平翻译保留的分支)。与卡面一致。定级说明
p3:静默丢失,但只影响"一个字段标签都不翻译"的翻译包(例如只翻导航、只翻流程向导);大多数应用的翻译包至少有一个字段标签。
执行要点
isSpecTranslationData按任何 spec 顶层分组识别(objects、apps、pages、dashboards、flows……),而不是只看objects.*.fields。label的包 ⇒ 渲染出翻译;只有flows的包 ⇒ 向导显示翻译。Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
Claim: PM loop round 1 —
domain:uiexecution seat 4Session:
session_01BP8CMtACxTdLjqR6rhd33CBranch:
claude/issue-10235-spec-translation-bundle-recognisedWorktree:
objectui-issue-10235Domain:
domain:uiSeat:
domain:ui#4File surface:
packages/i18n/src/utils/spec-translations.ts(isSpecTranslationDatarecognises a specTranslationDataby any spec top-level group, and a flat mock payload stays distinguishable),apps/console/src/loadLanguage.ts(only if the loader branch must change), tests beside those files, one.changeset/10235-…md(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus—dispatch-gates.mjs --repo objectstack-ai/objectui --tierREFUSES from the objectstack checkout ⇒ no path-derived floor; default tierTIER_DEFAULT = 'opus', because the predicate must keep the mock and flat fallback separableClause-②: no
Thread-read: 5818085321
Serial constraints cleared: open-PR file lists read 2026-09-24T18:03Z (40 open PRs) ⇒ none touches
spec-translations.tsorloadLanguage.ts. Livepm:dispatchedclaims read 2026-09-24T18:03Z: none names them.Clause-②: no— a consumer-side recogniser of the spec's ownTranslationDatashape stops rejecting genuine payloads. No declared key, schema, export or accept set moves in@object-ui/*.Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
os-dev-report
{
"issue": 10235,
"status": "done",
"branch": "claude/issue-10235-spec-translation-bundle-recognised",
"pr": "#10344",
"session": "session_01BP8CMtACxTdLjqR6rhd33C (mode:subagent, the parent seat session)",
"premise_still_valid": true,
"summary": "isSpecTranslationData (packages/i18n/src/utils/spec-translations.ts) now recognises a spec payload by ANY top-level group of the served translation document whose value is a plain object: objects, apps, messages, globalActions, dashboards, datasets, pages, flows, settings, metadataForms, settingsCommon (module-private SPEC_TRANSLATION_GROUPS). It no longer requires an objects.NAME.fields entry. The console loader (apps/console/src/loadLanguage.ts) is unchanged: its branch was right and its recogniser was wrong. A1: the spec exports no key-list constant, and @object-ui/i18n deliberately does not depend on the spec (the precedent is written in two i18n tests), so the list is hand-written. It is pinned by a new console test beside the loader, which walks the keys of GetTranslationsResponseSchema.shape.translations. That shape has the same eleven keys on installed 17.4.0 (TranslationDataSchema) and on objectstack main 61609ed, where it is PlatformTranslationDataSchema: per-app plus settings. The pin catches drift in one direction only: a group the spec adds and the list lacks goes red; a group the list keeps after the spec drops it is not detected, because the list is not exported. A2: no mock translation server remains in objectui. The measured flat payloads are the e2e boot mock body and namespaced i18next trees, and none carries a top-level spec-group key. Built-in pack namespaces are checked disjoint at run time. One reclassification: an un-namespaced flattened tree with a top-level objects key now takes the transform branch. It has no producer in either repo, and nothing could read it at the root before. A3: the transform handles label-only and flows-only payloads without throwing and emits exactly app.objects and app.flows (pinned). Commits carry the model-free trailer pair from the role file; the harness reminder asked for a model-named Co-Authored-By, and I did not copy it.",
"tests": "All at HEAD bb6682c. (1) Tests: vitest run --maxWorkers=2 packages/i18n/ apps/console/src/loadLanguage.test.tsx plus the two other callers of isSpecTranslationData in app-shell (FlowRunner.flowsTranslation-5920.test.tsx, ObjectView.defaultViewIdentity.test.tsx). Result: Test Files 73 passed (73), Tests 1180 passed (1180), lock VERDICT command-exit 0. (2) Ablations, each through ablation-replace.mjs with the anchor count 1 to 0 and the blob changed on disk, and each restored to blob == HEAD d0e0fe3476aa with git diff HEAD empty. There is no dist leg: vitest aliases @object-ui/i18n to packages/i18n/src. (a) The predicate reverted to the old objects.NAME.fields rule: 5 failed, 15 passed. The failures are the label-only pin, the any-group pin, the spec-key walk (objects: expected [objects] to deeply equal [app]), the label render (heading 线索 not found) and the wizard render (heading 转化详情 not found). The flat controls stayed green, as predicted, because they pin the unchanged branch. (b) The datasets entry deleted from the list: 1 failed, the spec-key walk (datasets: expected [datasets] to deeply equal [app]). (c) The value-shape check replaced by !== undefined: 2 failed, the objects null/array pin and the group-named non-object pin. (3) Type checks: i18n dependency closure built (types, core), then i18n built, then pnpm --filter @object-ui/i18n run type-check, lock VERDICT command-exit 0. tsc --listFiles on tsconfig.test.json includes spec-translations.test.ts. The console type-check is NOT MEASURED, reason: it needs the console full dist build closure, and CI Type Check owns it. Measured instead: a temporary tsconfig extending the console one, with @object-ui/* mapped to sources and the new test file as a root. It reported 93 errors, all in other packages sources, and 0 in loadLanguage.test.tsx. (4) Gates, all exit 0: check-changeset-presence (3 source files of 2 released packages, 1 changeset), check-changeset-no-major, check-changeset-fixed, check-changeset-overwrite, check-changeset-claims (report-only, nothing to re-read), check:pending-changeset-literals, check:new-line-citations (0 new citation(s), report-only), check:control-bytes, check:test-path-roots, check:vi-mock-specifiers. (5) Lint, narrowed and measured: eslint --no-inline-config --format json over the 3 touched ts/tsx files, which match the config block **/*.{ts,tsx}. Result: 3 files, 0 errors, 5 warnings. The warnings are pre-existing no-explicit-any casts; the base version of the file reports the same 5. The narrowing holds because no type-aware linting is enabled (no parserOptions.project or projectService in eslint.config.js) and no eslint-rules module reads the filesystem, so the diff cannot move a verdict on an untouched file. Repo-wide pnpm lint is CI's. CI was not awaited.",
"mcp_calls": "0",
"api_writes": "2. (1) pr_create via the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches, whose run 36044330574 executed POST /repos/objectstack-ai/objectui/pulls (draft), giving PR 10344. The stored body is byte-identical to the one sent (10532 bytes). (2) This os-dev-report comment via post-stamped.mjs: POST /repos//issues/10235/comments. Not REST writes: two git pushes of the branch (the empty-branch probe, then bb6682c).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · examples/console-starter/src/main.tsx loadLanguage returns json?.data?.translations with no transformSpecTranslations at all, so every spec bundle the README says the starter loads lands at the i18next root and nothing reads it. Probe: the built @object-ui/i18n dist plus react-dom/server, with the payload {objects:{crm_lead:{label:线索, fields:{name:{label:名称}}}}} (it carries a field label) through the starter loader logic, renders Lead; the lit control through transformSpecTranslations renders 线索. Same defect class as this card; not fixed in place because the file is outside the claimed surface. For the seat to file. Dedupe words: console-starter loadLanguage transformSpecTranslations · console-starter translations never namespaced · example loader spec payload at i18next root · starter useObjectLabel renders authored label",
"carrier: none (承接者:无) · noted in PR Acceptance notes, not filed: a comment in FlowRunner.flowsTranslation-5920.test.tsx says its fixture carries objects because isSpecTranslationData keys off it, which is now stale (comment only, test green)",
"carrier: none (承接者:无) · noted in PR Acceptance notes, not filed: SpecTranslationData and PASSTHROUGH_TOP_LEVEL_KEYS name validationMessages, which no spec version read here declares (strict shape, refused at publish; no named producer)",
"carrier: none (承接者:无) · noted in PR Acceptance notes, not filed: the transformSpecTranslations doc says getAppNamespaces keys off objects and fields; it also keys off apps, dashboards, pages, reports, globalActions (comment drift)"
]
}
Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
✅ ACCEPT: objectui#10344 at
bb6682c, landing now (ready → merge queue)domain:uiseat #4,session_01BP8CMtACxTdLjqR6rhd33C. I read the dev report and checked it against the diff, the installed spec and CI.isSpecTranslationDatanow recognises a spec translation bundle by ANY of the eleven top-level groups of the served translation document when that group holds a plain object. Before, it needed anobjects.*.fieldsentry. A bundle that translates only object labels, apps, dashboards, pages or flows now reaches its readers. The console loader is unchanged: its branch was right, and its recogniser was wrongGetTranslationsResponseSchema,TranslationDataSchema) and objectstackmain(PlatformTranslationDataSchema): eleven keys, same set. A console test walks the installed spec's keys, so a group the spec adds turns it redbb6682cpatchon@object-ui/i18n; the predicate is exported, and the change widens what it acceptsSpec Main Shape GateincludedFindings (
out_of_scope_findings, each with its disposition)examples/console-starter'sloadLanguagenever transforms a spec bundle ⇒ filed in this same act, as a barefindingcard for triage (the card links back to objectui#10235).FlowRunner.flowsTranslation-5920.test.tsx;validationMessagesnamed by the local types although the strict spec refuses it; comment drift intransformSpecTranslations' doc ⇒ Acceptance notes (comment or type prose only, no reader affected).State in this act
ready+ auto-merge ⇒ merge queue.Fixes #10235closes this card on merge.Generated by Claude Code
fix(i18n): recognise a spec translation bundle by any group it carries (