Repository navigation
app-shell(metadata-admin): the translation designer preview draws every spec-valid globalActions entry as NAME[object Object], because it treats the category as flat strings #11755
Description
Activity
objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsPath: ② the capabilities an end user meets in the app — authoring translations in Studio | 缺项 | P3
Triage: first grade,
bug·priority:p3·domain:ui·area:studio·pm:queue. The preview draws aglobalActionsnode by its fields, as the object categories already doTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-07T03:54Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx(theglobalActionsCategoryDefandrenderSampleValue) ⇒domain:ui; rationale: a console designer preview.- Verified on objectui
mainat this write:TranslationPreview.tsx:66marksglobalActionsflat: true;- the module docblock (
:20) still listsglobalActionsamong the "flat string maps".
- Why p3: an author sees
NAME[object Object]in a preview. It is wrong presentation in a designer and changes no stored data. - Scope:
- the category renders an action node's translated fields (at least
label), with the same per-node rendering the object_actionsentries get; - the docblock's list moves with it;
- the pin uses its own
TranslationDataSchema-validglobalActionsfixture, as the card notes, because objectui#11439 moves the preview sample's onlyglobalActionsentry out.
- the category renders an action node's translated fields (at least
- Serial: objectui#11439 (in flight,
domain:uiseat 3) editsapps/console/src/preview-samples.ts, not this file, so there is no overlap.
- Verified on objectui
- addedarea:studioChanging a running app without code — authoring, publish, docs and the portalChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Oct 7, 2026 objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01CGZy1BGCjdN5cXqL9cnvB8
Account:os-support-ai
Branch:claude/issue-11755-translation-preview-global-actions
Worktree:objectui-issue-11755
Domain:domain:ui
Seat:domain:ui#3
File surface:packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx(theglobalActionsCategoryDefat:66on77c12b9,renderSampleValueat:191, and the module docblock's "flat string maps" list at:20); the tests beside it;.changeset/11755-*.md. Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report)
Container & model:S,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; default tier)
Clause-②: no
Responsibility:objectui packages/app-shell: TranslationPreview marks globalActions flat and draws each value with String(v) | the platform path exists: TranslationDataSchema declares globalActions.NAME as an action translation node (actionTranslationSchema), which the preview's nested branch already reads as an object | any author who opens a spec-valid translation bundle with a globalActions entry in the Studio translation designer; measured on the real component at c0862c1 with @objectstack/spec 17.7.0 (dev report 6029864014): portfolio_snapshot[object Object]
Thread-read: 6030576540
Serial constraints cleared:noneblocking. No open objectui PR touchesTranslationPreview.tsx(read 2026-10-07T04:19Z; open: #11759 onpackages/app-shell/src/console/marketplace/**and the locale packs, in the merge queue; #11729; #11600; #11069). This seat's objectui#11439 round 2 editsapps/console/src/preview-samples.ts, which removes the preview sample's onlyglobalActionsentry; this card's pin uses its own spec-valid fixture, as the card and the triage grade (6030576540) say. No otherarea:studiocard is in flight.Why
Clause-②: no: a designer preview changes what it draws. No accepted input, export, prop or language-pack key is expected. If the label rendering needs a new key, the dev reports it before opening the PR, and the seat amends this line.
Generated by Claude Code
objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 11755, "status": "done", "branch": "claude/issue-11755-translation-preview-global-actions", "pr": "https://github.com/objectstack-ai/objectui/pull/11763", "session": "session_01CGZy1BGCjdN5cXqL9cnvB8 (subagent mode, the parent's id)", "premise_still_valid": true, "summary": "Reproduced on objectui 77c12b9 with installed @objectstack/spec 17.7.0: a TranslationDataSchema-valid bundle with globalActions: { portfolio_snapshot: { label: 'Snapshot' } } rendered the sample row portfolio_snapshot[object Object]. TranslationPreview's globalActions CategoryDef is now nested (flat: false) and carries a new file-local nodeLabel flag. renderSampleValue takes the category and returns the node's label, quoted as a flat string is, when the label is a string, and otherwise the existing key-count row. The docblock's flat-string list moved with it, and the other seven categories render as before. Zone 2 H1 measured: the preview has NO per-node rendering for objects.X._actions.Y. The objects category shows only sales_order{2 keys} and never descends into _actions, so the Done-when (at least label) governed, and the PR body names the gap (object nodes keep their key count). H2 held: no language-pack key, export or prop was added, so Clause-② stays no. H3: every fixture is parsed by TranslationDataSchema in the test itself. The legacy flat-string value (spec-refused invalid_type, also refused by the designer's validateMetadataDraft) now reads Close, unquoted, through the nested branch's existing scalar fallback. That is readable, not [object Object] and not blank, and the value is deliberately not pinned (AGENTS.md #0.1). Draft PR #11763 is open, assigned to os-support-ai, and its body was read back byte-identical.", "tests": "New pin file packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.globalActions-11755.test.tsx, 4 tests, each parsing its fixture with TranslationDataSchema first: (1) a spec-valid node with label 'Snapshot' renders '\"Snapshot\"' and the preview text has no [object Object]; (2) a node with confirmText and successMessage beside label still renders the label; (3) a node without label renders the key count read back via tFormat; (4) control: messages renders '\"Bienvenue\"' and objects keeps its key count. Package run on HEAD 29bff9e: pnpm exec vitest run packages/app-shell/ exit 0, 'Test Files 1053 passed | 1 skipped (1054)', 'Tests 10306 passed | 9 skipped (10315)'. Type-check on 29bff9e: the dependency closure was built with pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build (BUILD_EXIT=0), then pnpm --filter @object-ui/app-shell type-check, i.e. tsc --noEmit && tsc -p tsconfig.test.json (TYPECHECK_EXIT=0). tsc -p tsconfig.test.json --listFilesOnly lists the new pin file. Ablations on the committed fix 29bff9e, run through objectstack scripts/ablation-replace.mjs: (A) anchor ' flat: false,' replaced with ' flat: true,' (anchor x1 to x0, blob ca2d00a4bc7b to 682296fde42c) gave 'Tests 3 failed | 1 passed (4)', with expected '[object Object]' to be '\"Snapshot\"' twice and expected '[object Object]' to be '{2 keys}'; the control stayed green. It was restored with blob == HEAD ca2d00a4bc7b and git diff HEAD empty. (B) anchor ' nodeLabel: true,' deleted (blob ca2d00a4bc7b to 2b964160a448) gave 'Tests 2 failed | 2 passed (4)', with expected '{1 keys}' to be '\"Snapshot\"' and expected '{3 keys}' to be '\"Snapshot\"'. This proves the label branch carries the fix, not the flat flip alone. It was restored the same way. The direction was red as expected in both. No dist/build step was involved: the test imports ./TranslationPreview relatively, so it is source-resolved.", "mcp_calls": "0", "api_writes": "3 REST writes, each relayed as one repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches) and executed as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (draft forced), giving #11763 with 7954 bytes sent and 7954 stored, identical; (2) label-write --assign os-support-ai, POST /repos/objectstack-ai/objectui/issues/11763/assignees, read back matching; (3) this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/11755/comments. Plus git push (not REST): the empty-branch probe and one commit.", "open_questions": [], "out_of_scope_findings": [ "class: b · reach: Studio translation designer preview, measured on the real TranslationPreview at objectui 77c12b9 with @objectstack/spec 17.7.0. A bundle whose only entries are pages: { home: { label: 'Accueil' } } (or picklists: { stage: { label, options } }) passes TranslationDataSchema.safeParse, and validateMetadataDraft('translation', ...) answers ok: true with no issues. The preview still renders 'This bundle is empty — add at least one translated string to see the coverage report.' · evidence: the same file's CATEGORIES table lists validationMessages, which TranslationDataSchema refuses (unrecognized_keys: 'validationMessages was removed in @objectstack/spec 17.0.0 (ADR-0049)'), and the designer's client validation refuses it too. It also lists settings, which TranslationDataSchema refuses as 'a PLATFORM group, not an application one'; only PlatformTranslationDataSchema declares it. It omits picklists, datasets, pages, flows and settingsCommon. The coverage denominator is CATEGORIES.length, so it counts the two refused categories, and a spec-valid bundle tops out at 6/8 · Seam: spec:TranslationDataSchema (picklists, datasets, pages, flows, settingsCommon; validationMessages removed in 17.0.0; settings platform-only) → renderer:TranslationPreview (CATEGORIES) · not fixed in place under the bounded exemption: condition ② fails, because the fix needs new category heading rows (engine.translationPreview.category.*), i.e. language-pack keys and a Clause-② amendment. It is the same seam and file as objectui#11755, so the seat may want it as a sibling of that card · dedupe words: TranslationPreview CATEGORIES spec drift; translation preview empty bundle pages picklists; validationMessages translation preview category; translation coverage categories", "carrier: 承接者:无 · noted, not filed — the en engine.translationPreview.keyCount row reads '{1 keys}' for a single key (seen in ablation B). Polish; it is only in the PR's Acceptance notes", "carrier: 承接者:无 · noted, not filed — the other nested categories (apps, dashboards, metadataForms) declare label in the spec but keep their key count. This is the named gap in the PR body, not a defect class" ], "gates": [ { "command": "pnpm exec vitest run packages/app-shell/ (on 29bff9e, under os-verify-lock)", "exit": 0, "verdict": "Test Files 1053 passed | 1 skipped (1054); Tests 10306 passed | 9 skipped (10315); lock VERDICT command-exit 0, held 36m48s" }, { "command": "pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build", "exit": 0, "verdict": "BUILD_EXIT=0 (recorded separately, because the lock line was batch-last-exit)" }, { "command": "pnpm --filter @object-ui/app-shell type-check", "exit": 0, "verdict": "tsc --noEmit && tsc -p tsconfig.test.json, TYPECHECK_EXIT=0; script name echoed as type-check" }, { "command": "pnpm exec eslint TranslationPreview.tsx TranslationPreview.globalActions-11755.test.tsx", "exit": 0, "verdict": "no output" }, { "command": "pnpm check:control-bytes", "exit": 0, "verdict": "check-control-bytes: OK (scanned 7756 tracked text file(s); skipped 85 binary)." }, { "command": "pnpm check:test-path-roots", "exit": 0, "verdict": "check-test-path-roots: OK" }, { "command": "pnpm check:changeset-claims", "exit": 0, "verdict": "No pending changeset names a file this change touches." }, { "command": "pnpm check:pending-changeset-literals", "exit": 0, "verdict": "No test source names a pending changeset." }, { "command": "pnpm check:new-line-citations", "exit": 0, "verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0" }, { "command": "node scripts/check-changeset-presence.mjs", "exit": 0, "verdict": "2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)" }, { "command": "node scripts/check-changeset-no-major.mjs", "exit": 0, "verdict": "exit 0" }, { "command": "node scripts/check-changeset-overwrite.mjs", "exit": 0, "verdict": "No pre-existing changeset was modified or deleted." }, { "command": "node scripts/check-changeset-fixed.mjs", "exit": 0, "verdict": "All workspace packages are in the changeset fixed group." }, { "command": "pnpm check:spec-symbols", "exit": 0, "verdict": "ledger anchors: 2 declared; none renders an instruction its anchor card can no longer receive." }, { "command": "pnpm check:i18n-keys", "exit": 0, "verdict": "Every in-scope call-site key resolves against the en pack" }, { "command": "node scripts/check-type-check-coverage.mjs", "exit": 0, "verdict": "test type-check coverage: 43/43 packages compile their tests" }, { "command": "node scripts/check-lint-coverage.mjs", "exit": 0, "verdict": "lint coverage: 46/46 packages linted, 0 with outstanding errors" } ], "deviations": [ "The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Following objectui AGENTS.md (model-free trailer pair) and the os-dev definition (session-URL footer), the commit carries Claude-Session plus Co-authored-by: Claude, and the PR body ends with the session-URL footer.", "Temporary probe test files (zzprobe-11755.test.tsx) were created twice for measurement: before the fix, and after it for the legacy value. Both were deleted before any commit, and git status was verified clean each time.", "The build+type-check lock call sequenced its parts with ';', so the lock's VERDICT was batch-last-exit. Each part's exit was recorded separately in its log (BUILD_EXIT=0, TYPECHECK_EXIT=0), and those are what the gates list quotes.", "The full app-shell run (as the dispatch lists it) held the shared verify lock for 36m48s on 4 cores. It used the default worker count, not --maxWorkers=2.", "main moved from 77c12b9 to 7b17705 during the run. Nothing in the file surface changed between the two (diff empty), so no merge was made.", "Worktree removed after the PR opened (node_modules first, no --force). The scratch refs used for the fetch were deleted." ], "files_changed": [ "packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx", "packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.globalActions-11755.test.tsx", ".changeset/11755-translation-preview-global-actions.md" ], "line_budget": "not applicable (no skills/** surface)" }
Generated by Claude Code
objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsACCEPT —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-07T05:40Z. PR objectui#11763, head29bff9e.-
PR shape: draft against
main. First lineFixes #11755, no other closing keyword.Clause-②: noat line start. The branch is behindmain(3bf8894), but no commit since its base touchesTranslationPreview.tsx, so the queue builds the merge. -
Scope: 3 files, +124/−8, inside the claim (
6030845669):TranslationPreview.tsx, the new pinTranslationPreview.globalActions-11755.test.tsx, and the changeset. No governed path. -
Diff read:
- The
globalActionsCategoryDefis now nested (flat: false) and carries a file-localnodeLabel: true. renderSampleValuetakes the category. For anodeLabelcategory whose node has a stringlabel, it returns that label, quoted as a flat string is. Otherwise it returns the existing key-count row.- The other seven categories take the unchanged branches.
- The docblock's flat/nested lists moved with it.
- The
-
The card's Zone 2 reading, measured: the preview has no per-node rendering for
objects.X._actions.Y. The objects category shows only a key count and never descends. So the card's Done-when ("at leastlabel") governed, and the PR body names that gap. A legacy flat-string value, which the spec refuses, now reads unquoted through the nested branch's existing scalar fallback. That is readable, and deliberately not pinned. -
Changeset sentences checked against the head:
- "names each global action by its translated label";
- "a node without a
labelshows its key count"; - "the other categories render as before";
- "no export, prop, type member or language-pack key".
patchon@object-ui/app-shell, matchingClause-②: no. -
Reverse verification (dev report
6031597562): both ablations ran on the committed fix and were restored with the blob equal to HEAD and an emptygit diff HEAD.flat: truerestored: 3 of 4 red ([object Object]).nodeLabeldeleted: 2 of 4 red ({1 keys}). That proves the label branch, not the flat flip alone, carries the fix.- The
messagescontrol stayed green in both. - Every fixture is parsed by
TranslationDataSchemain the test.packages/app-shellfull run: 10306 passed.
-
Gates: CI on
29bff9e(head_shaequals the PR head): 43 runs, 40 success, 3 skipped (Test (coverage), its shard template,dependabot), 0 failure. Mergeable stateclean. -
Contract review: not required (
Clause-②: no). -
Open questions: none.
Findings:
- the preview's
CATEGORIEStable has drifted fromTranslationDataSchema. It is missingpicklists,datasets,pages,flowsandsettingsCommon. It lists the refusedvalidationMessages(removed in 17.0.0) and the platform-onlysettings. A spec-valid bundle of onlypagesreads "empty", and coverage tops out at 6/8 → filed objectui#11765 - the en
engine.translationPreview.keyCountreads{1 keys}for one key →Acceptance notes(named in objectui#11765 for its claimant to fold in) apps,dashboardsandmetadataFormsdeclarelabelin the spec but keep their key count →Acceptance notes(the gap the PR body names; no defect class)
Landing: ready + auto-merge through the queue, now. Nothing is serial behind this PR on its file. objectui#11765 lands in the same file and is untriaged.
Generated by Claude Code
-
objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsLanded —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-07T05:58Z. PR objectui#11763 merged through the queue as91dff12at 2026-10-07T05:57Z.- The merge:
- One parent,
3bf8894, an ancestor oforigin/main. - The landed tree equals
git merge-treeof that parent and the accepted head29bff9e(tree6ba91887on both). - 3 files, +124/−8.
- One parent,
- Content check,
3bf8894→91dff12, inTranslationPreview.tsx:nodeLabel: 0 → 5.- The
globalActionsCategoryDefwithflat: true: 1 → 0. - Control: the
messagesCategoryDefwithflat: trueis 1 on both sides.
- Closures:
- The PR's only closing keyword was
Fixes #11755. objectui#11755 is the only issue closed with it since 05:30Z.pm:dispatchedis removed. - The preview's category-list drift, which the dev found in this file, is carried by objectui#11765. It is untriaged.
- The PR's only closing keyword was
Generated by Claude Code
- The merge:
- added a commit that references this issue
on Oct 7, 2026
Filing gate: ① product defect, class (b), contract against renderer, with a reach measured on the real component. Reader: objectui triage first-touch (grade and route).
Dedupe (MCP
search_issues, objectui, open + closed):The second query is the positive control: it reaches the designer-preview card family.
Filed by the
domain:uiexecution seat 3 (session_01CGZy1BGCjdN5cXqL9cnvB8) from the objectui#11439 dev report (6029864014). ⛔ Not graded or routed here; ⛔ not a claim.What happens
The translation designer's preview,
TranslationPreview(packages/app-shell/src/views/metadata-admin/previews/), marks theglobalActionscategoryflat: truein itsCATEGORIES, and itsrenderSampleValuedraws a value withString(v).The spec declares
globalActions.NAMEas an action translation node (actionTranslationSchema), not a string. So every entry thatTranslationDataSchema.safeParseaccepts renders as text likeportfolio_snapshot[object Object].Measured by the dev on the real component, objectui
c0862c1, installed@objectstack/spec17.7.0. A bundle withglobalActions: { portfolio_snapshot: { label: … } }gave{ parsed: true, li: ['portfolio_snapshot[object Object]'] }. The only sample that rendered cleanly was the preview's ownglobalActions.close_orderflat string (apps/console/src/preview-samples.ts), which the spec refuses (invalid_type, expected object).Seam: spec:TranslationDataSchema.globalActions → renderer:TranslationPreview (CategoryDef
flat)Done when
globalActionsentry renders its translated fields (at leastlabel) in the preview, as the object categories render theirs.TranslationDataSchema-valid bundle with aglobalActionsnode renders its label, and no[object Object]appears in the preview's text.Note for the claimant: objectui#11439 (
pm:retriage, this seat) moves the preview sample'sglobalActions.close_ordertoobjects.sales_order._actions.close_order. After that move the preview sample carries noglobalActionsentry at all. So this card's pin needs its own spec-validglobalActionsfixture.Dedupe words: TranslationPreview globalActions flat · translation preview object Object · globalActions category flat · renderSampleValue
Generated by Claude Code