Repository navigation
fix(i18n): a bound action's copy is read from its own object, never from globalActions (objectui#11439) - #11764
Merged
objectstack-fleet[bot] merged 3 commits intoOct 7, 2026
Conversation
…longer reads globalActions actionSuffixes reads OBJECT._actions.ACTION.* for a key object and globalActions.ACTION.* only when there is none, mirroring actionTranslationNode in @objectstack/spec 17.7.0. useActionTextLocalizer keys on the action's declared objectName, else the caller's host. The objectui#3372 pin's two bound-action cases are re-pointed to the object-scoped copy (triage's amended ruling); the object-less case is unchanged. The console translation sample files close_order under objects.sales_order._actions and writes fields.amount as a label node. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…bject only Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
… cast Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-11439-bound-action-label
branch
October 7, 2026 06:01
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11439
Clause-②: no
What changes
An action's translated copy is read from ONE bundle node, chosen by the object the action belongs to, the way
@objectstack/spec17.7.0 reads it (actionTranslationNodebehindlookupActionField). This implements triage's amended ruling on this card (comment 6030552631, amending 5942994297), route B.packages/i18n/src/useObjectLabel.ts,actionSuffixes: a key object readsobjects.OBJECT._actions.ACTION.TAILonly. No key object readsglobalActions.ACTION.TAILonly. The object-then-global chain objectui#3372 added is gone. The eight action resolvers (actionLabel,actionConfirm,actionSuccess,actionOutcome,actionDescription,actionResultDialog,actionParamText,actionParamOptionLabel) keep their signatures, and their docblocks now state the rule.getAppNamespacesstill countsglobalActionsas an app scope, because object-less actions need it.packages/react/src/hooks/useActionTextLocalizer.ts: the key object is the action's declaredobjectName, else the host the caller passes. That is whattranslateObjectstamps on an embedded action. It is also what the runtime-side resolvers inuseConsoleActionRuntimeandRecordDetailViewalready compute for the description, params and result dialog. No parameter is added:objectNameis a spec key the action object already carries.packages/i18n/src/__tests__/useObjectLabel-globalAction.test.tsx: the docblock is corrected. The two bound-action cases (actionLabel/actionSuccessoncrm_case/log_call) are re-pointed to the object-scoped copy, each with a comment citing 6030552631. The case "still resolves a globalAction when objectName is omitted" is byte-identical.apps/console/src/preview-samples.ts, thetranslationsample:close_order, which theactionsample binds tosales_order, moves fromglobalActions.close_ordertoobjects.sales_order._actions.close_orderas a{ label }node.fields.amountbecomes a{ label }node..changeset/11439-bound-action-copy.md: patch on@object-ui/i18nand@object-ui/react.Behaviour change, in plain words: a bound action's copy filed under
globalActionsno longer applies, on any of the action's texts. Move it toobjects.OBJECT._actions.ACTION. An action with no object still readsglobalActions.No input, export, prop or language-pack key is added.
Dispatch sites measured for the key-object rule
Every caller of
useActionTextLocalizerpasses its host object and draws actions from that object's ownactionsarray, so a declaredobjectNamethere equals the host or is absent:DeclaredActionsBar: itsobjectNameprop. The dispatch stamps that host asobjectName.ObjectView, toolbar and row actions:objectDef.name.RecordDetailView, record actions:objectDef.name.RelatedRecordActionsBridge:childDef.name. The dispatch stamps the child object asobjectName.record:quick_actionsandrecord:related_list: the context object, orundefinedwhen there is none.page:header:ctx.objectName, with ids resolved against that object's actions.PermissionMatrixEditor:PERMISSION_SET_OBJECT.None of these files changes. With the localizer reading the declared
objectNamefirst, each one already reachesactionSuffixeswith the key object. The localizer line changes one case: an action that declaresobjectNamebut is drawn where the caller passes no object. It now reads its own object's copy instead ofglobalActions.Census of objectui's own bundles
git grep -c globalActionsover the tree at77c12b9, CHANGELOGs excluded, finds one bundle-data hit: the preview sample'sclose_order, which is bound and moves here. It also serves as the live positive control, since the card's census comment named it. The spec-translations passthrough fixture'sglobalActions.savenames no declared action, so it is object-less and stays. The other hits are resolver code, UI chrome (theTranslationPreviewcategory) and catalogue keys.Sample parse:
TranslationDataSchemafrom@objectstack/spec/system17.7.0objects.sales_order.fields.amount: invalid_typeandglobalActions.close_order: invalid_type.The new
apps/console/src/__tests__/preview-samples-translation-data-11439.test.tspins this. In the spec-valid test, the sample stays inKNOWN_STALEfor its record-vs-collection question. This PR does not touch that question, and that test's reverse row stays green.Tests
New pins:
globalActions, all eight resolvers keyed oncrm_caseshow the authored text. With no key object, they read the global node (control).packages/react/src/hooks/__tests__/useActionTextLocalizer.keyObject-11439.test.tsx:objectNameis the key when the caller passes no host;objectNamewins over the caller's host;globalActions.Ablations. Each went through
ablation-replace.mjs. The mutation landing was shown by anchor count and blob hash. The restore was proven by blob equal to HEAD and an emptygit diff HEAD. The subjects resolve through vitest's source alias, so no build sits in between.actionSuffixesturns 4 pins red: the pin file's "never reads globalActions copy for a bound action" (expected '合并工单' to be 'Merge Cases') and three localizer pins (expected '关闭订单(全局)' to be 'Close Order').objectNameturns 2 pins red: "the declared objectName is the key even where the caller knows no object" and "the declared objectName wins over the host the caller passes".fields.amountput back as a flat string turns the parse pin red (objects.sales_order.fields.amount: invalid_type).Local gates
All runs are at HEAD
48858e9, from the worktree root.pnpm exec vitest run --maxWorkers=2 packages/i18n/ packages/react/plus the action-copy consumers:page-header-action-i18n,page-header-action-ids,record-quick-actions.actionText-i18n,record-quick-actions.resultDialog,RecordRelatedListRenderer.authoredActions-11163,record-alert.resultDialog,DeclaredActionsBar,useConsoleActionRuntime.overrideNotice, and the four consolepreview-samples-*tests. Result:Test Files 202 passed (202),Tests 2835 passed | 13 skipped (2848).pnpm --filter @object-ui/i18n type-checkexit 0 andpnpm --filter @object-ui/react type-checkexit 0, afterpnpm --filter '@object-ui/react^...' build. Each package's test project lists the touched test (--listFilesOnly).@object-ui/consoletype-check: NOT MEASURED locally. It resolves every workspace dependency through builtdist, so it needs the whole console closure built. It is declared to CI's Type Check job. In its place, a narrowedtscran over the six touched source and test files with root paths extended to sources (200 workspace files in the program) and exited 0. A deliberately mistyped control file added to the same program went red with TS2322.pnpm exec eslinton the six touched files: 0 errors. There are no new warnings: the localizer'sas anycount is 8, as on the base.check:control-bytes,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:new-line-citations(0 new) andscripts/check-changeset-presence.mjs: all exit 0.Acceptance notes
objectNamedifferent from the object whoseactionsarray carries it.translateObjectdoes.DeclaredActionsBar,RelatedRecordActionsBridgeand the clone dispatch stamp the host as the dispatchedobjectName, so the runtime-side description, params and result dialog would key on the host.globalActions: theaction.zod.tsdocblock, theglobalActionsdocblock intranslation.zod.ts, andpackages/spec/liveness/action.json. The landing comment names them for the objectstackdomain:speclane;globalActionsfallback (git grepovercontent/, the two package READMEs andskills/), so none changes.origin/mainmoved to7b17705(fix(app-shell): Installed Apps reads a package the runtime refused to load as not loaded (objectui#11645) #11759) after this branch's base. That commit touches none of these files.Session:
https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8Generated by Claude Code